セキュリティブログ

App Sandbox Escape vulnerability in macOS (CVE-2023-27966)

App Sandbox Escape vulnerability in macOS (CVE-2023-27966)

更新日:2024.05.11

Introduction

I am Masahiro Kawada, Penetration Testing Section, Offensive Security Department.

Here, I report a macOS vulnerability that allows for App Sandbox escaping. Following the release of this information in August 2023, I write a blog post detailing the process of discovering the vulnerability.

CVE-2023-27966

Motivation and Purpose

I initiated this research with the aim of developing a C2 agent in Word document format.
To utilize a C2 agent in Word document format on macOS, it is imperative to bypass the App Sandbox. As expected, the Sandbox is designed to be difficult to penetrate. Therefore, I conducted an investigation to determine the most effective method to achieve this.

App Sandbox

The App Sandbox serves as a mechanism to limit app access in the event that an attacker compromises app permissions. Adherence to Sandbox settings is compulsory, particularly when distributing apps via the App Store. For each Entitlement, please consult the official documentation.
Reference: App Sandbox

For instance, the Slack.app downloaded from the App Store and the Slack.app downloaded from the official website exhibit certain differences in settings at the time of build, including Sandbox settings.

# App Store (https://apps.apple.com/jp/app/slack-for-desktop/id803453959)
➜  ~ codesign -dvv --entitlements - /Applications/Slack.app
...
CodeDirectory v=20400 size=477 flags=0x0(none) hashes=4+7 location=embedded
...
[Dict]
    [Key] com.apple.security.app-sandbox
    [Value]
        [Bool] true
...

# https://slack.com/intl/ja-jp/downloads/mac
➜  ~ codesign -dvv --entitlements - /Applications/Slack.app
...
CodeDirectory v=20500 size=485 flags=0x12000(library-validation,runtime) hashes=4+7 location=embedded
...
[Dict]
...

If you are developing with Xcode, the App Sandbox can be configured as follows:

App Sandbox Settings

Container Directory

Apps with Sandbox are assigned a dedicated container directory, ~/Library/Containers/<Bundle ID>. Here, the Bundle ID is a unique identifier assigned at the time of app build and can be easily identified.

➜  ~ codesign -dvv --entitlements - /Applications/Microsoft\ Word.app
Executable=/Applications/Microsoft Word.app/Contents/MacOS/Microsoft Word
Identifier=com.microsoft.Word
...

Primarily, apps with Sandbox applied utilize the Data directory within each container directory.

➜  ~ ls -al ~/Library/Containers/com.microsoft.Word/Data
total 16
lrwxr-xr-x   1 victim  staff    31B Jan  9  2020 .CFUserTextEncoding -> ../../../../.CFUserTextEncoding
-rw-r--r--@  1 victim  staff   6.0K Mar 15 17:32 .DS_Store
lrwxr-xr-x   1 victim  staff    19B Jan  9  2020 Desktop -> ../../../../Desktop
drwx------   2 victim  staff    64B Nov 21  2020 Documents
lrwxr-xr-x   1 victim  staff    21B Jan  9  2020 Downloads -> ../../../../Downloads
drwx------  34 victim  staff   1.1K Feb  9  2023 Library
lrwxr-xr-x   1 victim  staff    18B Jan  9  2020 Movies -> ../../../../Movies
lrwxr-xr-x   1 victim  staff    17B Jan  9  2020 Music -> ../../../../Music
lrwxr-xr-x   1 victim  staff    20B Jan  9  2020 Pictures -> ../../../../Pictures
drwx------   2 victim  staff    64B Nov 21  2020 SystemData
drwx------   2 victim  staff    64B Nov 21  2020 tmp

Although each app can use this container directory with relative freedom, it is subject to certain restrictions. For instance, binaries and shell scripts located within the container directory cannot be executed.

➜  ~ /sbin/ping -c 1 127.0.0.1
PING 127.0.0.1 (127.0.0.1): 56 data bytes
64 bytes from 127.0.0.1: icmp_seq=0 ttl=64 time=0.055 ms

--- 127.0.0.1 ping statistics ---
1 packets transmitted, 1 packets received, 0.0% packet loss
round-trip min/avg/max/stddev = 0.055/0.055/0.055/0.000 ms
➜  ~ cp /sbin/ping ~/Library/Containers/com.microsoft.Word/Data/
➜  ~ ~/Library/Containers/com.microsoft.Word/Data/ping -c 1 127.0.0.1
[1]    27348 killed     ~/Library/Containers/com.microsoft.Word/Data/ping -c 1 127.0.0.1
➜  ~ open -a ~/Library/Containers/com.microsoft.Word/Data/ping --args -c 1 127.0.0.1
The application /Users/victim/Library/Containers/com.microsoft.Word/Data/ping cannot be opened for an unexpected reason, error=Error Domain=RBSRequestErrorDomain Code=5 "Launch failed." UserInfo={NSLocalizedFailureReason=Launch failed., NSUnderlyingError=0x6000007d4360 {Error Domain=NSPOSIXErrorDomain Code=162 "Unknown error: 162" UserInfo={NSLocalizedDescription=Launchd job spawn failed}}}

Microsoft Word Example

Upon checking the Sandbox settings in Microsoft Word.app, you will encounter the following unique settings.

➜  ~ codesign -dvv --entitlements - /Applications/Microsoft\ Word.app
...
    [Key] com.apple.security.temporary-exception.sbpl
    [Value]
        [Array]
            [String] (allow file-read* file-write* (require-all (vnode-type REGULAR-FILE) (regex #"(^|/)~\$[^/]+$")) )
            [String] (deny file-write* (subpath (string-append (param "_HOME") "/Library/Application Scripts")) (subpath (string-append (param "_HOME") "/Library/LaunchAgents")) )
...

The first line of the rule allows reading and writing to files beginning with ~$, which is added so that the necessary files can be generated when editing Word documents.
The second line restricts writing to the ~/Library/Application Scripts/ and ~/Library/LaunchAgents/ directories. This was added in response to a previous vulnerability and will be explained later.

Establishing a C2 session from Microsoft Word.app with Sandbox applied is not practical, as it cannot even enumerate the /tmp/ directory. We need to escape from Sandbox to bring this to a practical level of access rights.

Enumeration under /tmp/

In Microsoft Word.app's Sandbox escaping, it is important to keep in mind that it is possible to read and write files that begin with ~$.

In order to verify from the Sandbox environment applied to Microsoft Word.app, I prepared a C2 server using Mythic and Apfell, and established a C2 session using a simple macro as shown below.

Example of Macro

C2 Session Established

launchd and Sandbox Escaping

Since the defined Sandbox profile applies to child processes, it is necessary to create a process that is completely independent of the current process in order to perform Sandbox escaping. In principle, due to the protection by SIP (System Integrity Protection), it is difficult to inject code into another process.

Therefore, it is almost mandatory to use launchd when attempting to escape from the Sandbox.
Launchd is a framework responsible for system initialization and service management, and /sbin/launchd is always assigned a process ID of 1.

You can create a child process of launchd mainly in the following two ways.
● /usr/bin/open command
● Persistence methods such as LaunchAgents, LaunchDaemons, or Login Items

The creation of child processes of /sbin/launchd utilizing the open command can be easily confirmed as follows.

➜  ~ open -a /sbin/ping --args -c 32 127.0.0.1
➜  ~ ps -ef
  UID   PID  PPID   C STIME   TTY           TIME CMD
  ...
  501 12024     1   0  6:35PM ??         0:00.01 /sbin/ping -c 32 127.0.0.1
  ...

The following are two macOS vulnerabilities that were capable of Sandbox escaping by these methods.

Sandbox Escape via /usr/bin/open

● CVE-2021-30864 (Perception Point)
With this vulnerability, Sandbox escaping is achieved by modifying the HOME directory by setting an environment variable when launching Terminal.app with the open command, and then letting it execute .zshenv or other files placed by the attacker. Terminal.app is executed via launchd by the open command, so it is not affected by Sandbox.

➜  ~ open --env __OSINSTALL_ENVIROMENT --env HOME=~/Library/Containers/com.microsoft.Word/Data /System/Applications/Utilities/Terminal.app

It is now fixed so that the environment variable HOME cannot be rewritten via the env option of the open command.

Terminal.app Launched via Microsoft Word.app

Checking the Environment Variable

Reference: A Technical Analysis of CVE-2021-30864: Bypassing App Sandbox Restrictions

● CVE-2022-26706 (Microsoft)
The vulnerability allows Sandbox escaping by executing a Python script using the open command. Files generated by Sandbox-enabled apps, such as Microsoft Word.app, are assigned the com.apple.quarantine attribute. In this case, they cannot be executed as Shell or Python scripts. However, this restriction was successfully circumvented using the -stdin option of the open command.

deploy@deploy ~ % ls -l test.py
-rwxr-xr-x@ 1 deploy  staff  77 Aug 31 22:07 test.py
deploy@deploy ~ % xattr test.py 
com.apple.quarantine
deploy@deploy ~ % ./test.py 
zsh: operation not permitted: ./test.py

This vulnerability has been fixed to prevent the open command from creating a process even if the file specified in the -stdin option has the com.apple.quarantine attribute.

➜  ~ xattr hello.py
com.apple.quarantine
➜  ~ open --stdin='hello.py' -a Python
The application /Applications/Xcode.app/Contents/Developer/Library/Frameworks/Python3.framework/Versions/3.9/Resources/Python.app cannot be opened for an unexpected reason, error=Error Domain=NSOSStatusErrorDomain Code=-10810 "kLSUnknownErr: Unexpected internal error" UserInfo={_LSFunction=_LSLaunchWithRunningboard, _LSLine=2735, NSUnderlyingError=0x60000232cb40 {Error Domain=RBSRequestErrorDomain Code=5 "Launch failed." UserInfo={NSLocalizedFailureReason=Launch failed., NSUnderlyingError=0x60000232cae0 {Error Domain=NSPOSIXErrorDomain Code=1 "Operation not permitted" UserInfo={NSLocalizedDescription=Launchd job spawn failed}}}}}

Reference: Uncovering a macOS App Sandbox escape vulnerability: A deep dive into CVE-2022-26706

Sandbox Escaping by Persistence

● CVE Unnumbered (MDSec)
This is a vulnerability related to the second line of the custom rule applied to Microsoft Word.app that we have just mentioned.

[String] (deny file-write* (subpath (string-append (param "_HOME") "/Library/Application Scripts")) (subpath (string-append (param "_HOME") "/Library/LaunchAgents")) )

As this rule was not implemented at the time, Microsoft Word.app could generate a plist file in the ~/Library/LaunchAgents directory if the name of the file started with ~$.

The ~/Library/LaunchAgents directory contains plist files. These are similar to configuration files in macOS and can be stored in either XML or binary format. As an example, the following plist file can be saved to execute specified commands when a user logs in.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>Label</key>
    <string>LaunchAtLogin</string>
    <key>ProgramArguments</key>
    <array>
        <string>/bin/sleep</string>
        <string>300</string>
    </array>
    <key>RunAtLoad</key>
    <true/>
</dict>
</plist>

When the user logs in after storing the plist file, it can be confirmed that the specified sleep command is executed as a child process of launchd.

Executing a Command Registered in LaunchAgents

The directory is not affected by the Sandbox applied to Microsoft Word.app, as it is writable by a general user and launchd is responsible for the execution of the specified command.

Of course, the aforementioned custom rules have been added to address this vulnerability.

Reference: Escaping the Sandbox – Microsoft Office on MacOS

● CVE Unnumbered (Madhav Bhatt)
This is a Sandbox escape that makes use of Login Items, which was still working at the time of this study. It is not something that can be used for escaping from the Sandbox of any app, but it is valid in Microsoft Word.app, which can generate files starting with ~$.

The premise is that it is possible to add Login Items even from apps that have Sandbox applied; apps and files can be added to Login Items, and if a file is added, the app will be opened by the target file's default handler when the user logs in. For example, if a zip file is added to Login Items, the file will be unzipped by Archive Utility.app.

By utilizing this specification, it is possible to achieve sandbox escaping.
First, create a ~$exploit.zip under the user's home directory and add it to Login Items. This zip file will be extracted to .zshenv. The ~/.zshenv file does not exist in macOS by default and the specified OS command will be executed when zsh starts. Note that if you unzip it as an already existing .zshrc, it will be unzip into a file name such as .zshrc 2.
Next, add Terminal.app to the Login Items.

Adding Login Items

Added Login Items

OS commands will then be executed on Terminal.app when a user logs in according to the following procedure.

  1. The ~/~$exploit.zip added to Login Items is extracted to ~/.zshenv.
  2. The Terminal.app added to Login Items is started
  3. zsh launched by Terminal.app reads ~/.zshenv and the specified OS command is executed

Sandbox Escaping

Reference: Office365 MacOS Sandbox Escape

However, there is one challenge when it comes to actually exploiting this method.
As the decompression of the zip file in step 1 and the launching of Terminal.app in step 2 are executed simultaneously (in no particular order) when the user logs in, there are cases where Terminal.app is launched before the zip file is decompressed to ~/.zshenv at the first login. In this case, you need to wait for a second login. It is a major disadvantage, given that it is possible to force a logout with commands such as launchctl bootout gui/$UID only once.

Failure to Execute the Command

Therefore, I conducted an investigation to find a more effective method for sandbox escaping, based on this approach.

Terminal File

The Terminal file is in XML format, which is referred to as the Terminal configuration file in macOS. The default handler for this file is Terminal.app, and it is possible to execute OS commands specified in the file from Terminal.app as follows.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
    <dict>
        <key>CommandString</key>
        <string>sleep 30</string>
        <key>ProfileCurrentVersion</key>
        <real>2.0600000000000001</real>
        <key>RunCommandAsShell</key>
        <false />
        <key>name</key>
        <string>exploit</string>
        <key>type</key>
        <string>Window Settings</string>
    </dict>
</plist>

Default Handler for Terminal Tile

Execution of Terminal File

/usr/bin/open + .terminal

When the terminal configuration file touch.terminal was placed under ~/Library/Containers/com.microsoft.Word/Data/ using Microsoft Word.app and the open command was executed, the execution failed as follows.

Execution of the open Command

Execution of Terminal File Failed

As mentioned when describing CVE-2022-26706, which was discovered by Microsoft, files created in the system by Microsoft Word.app with Sandbox applied are given the com.apple.quarantine attribute and the constraints are strictly enforced. In principle, it is difficult to remove this attribute without escaping the Sandbox, but for investigation purposes, I removed it from Terminal.app and ran the open command again.

deploy@deploy ~ % xattr –d com.apple.quarantine ~/Library/Containers/com.microsoft.Word/Data/touch.terminal

The execution failed with a different error message. The error message and its behaviour suggest that it is difficult to open a terminal file from a process to which Sandbox has been applied. As it could be a constraint applied to the container directory, a new ~/~$touch.terminal was created in the home directory, but the result was similar.

Failed to Execute the Terminal File

No further investigation was carried out as both the constraints of the com.apple.quarantine attribute and the Sandbox applied process needed to be breached.

Login Items + .terminal

Next, an attempt was made to run the terminal file by adding it to the Login Items.
This time, as I was using Apfell's C2 session, I added Login Items using JavaScript for Automation. The easiest way to do this is to use Apfell's persist_loginitem_allusers function, as described in Madhav Bhatt's article.

I then used the launchctl command to force the user to log out, which opened the terminal file at login and confirmed that the specified OS command was executed on Terminal.app. Terminal.app is not affected by the sandbox applied to the Microsoft Word.app, so it was successfully escaped from the App Sandbox at this point.

Forced Logout via launchctl Command

Sandbox Escape

Timeline

Date and Time Remarks
2022/10/01  Vulnerability reported
2023/03/27 macOS Ventura 13.3 releaseed with security patches
2023/04/14 Reward amount notification
2023/05/01 Vulnerability disclosed
2023/05/24 Reward payed
2023/08/01 Vulnerability information updated

Reference: About the security content of macOS Ventura 13.3

セキュリティ診断のことなら
お気軽にご相談ください
セキュリティ診断で発見された脆弱性と、具体的な内容・再現方法・リスク・対策方法を報告したレポートのサンプルをご覧いただけます。
経験豊富なエンジニアが
セキュリティの不安を解消します

Webサービスやアプリにおけるセキュリティ上の問題点を解消し、
収益の最大化を実現する相談役としてぜひお気軽にご連絡ください。

疑問点やお見積もり依頼はこちらから

お見積もり・お問い合わせ

セキュリティ診断サービスについてのご紹介

資料ダウンロード