CVE-2021-30798: TCC Bypass Again, Inspired By XCSSET

My team and I posted the details of the brand new Mac Malware XCSSET last year [1] [2], and disclosed the interesting 0 day tricks used inside. All the XCSSET payload modules were reviewed carefully. However, I was a newbie for hunting macOS vulnerability and I didn’t realize the TCC bypass is a vulnerability at that time until Jamf posted their new blog and detailed the 3rd 0 day used by XCSSET.

Then I did some research on the TCC framework, and here is the partial result.

TCC bypass used by XCSSET

The details can be read from the Jamf blog. Here is a simple summary :

In the payload module screen_sim.applescript, the malware looks up the applications that already have the Screen Recording permission, and then put its malicious application into the “Contents/MacOS” folder. All is done, it is just parasitic in the legitimate application to get the permission.

The new bypass idea

During the research of the TCC framework, I know that the TCC configuration data are stored in the sqlite3 database /Library/Application Support/com.apple.TCC/TCC.db, and the table for permission access is like this :

image-20210723153034982

The column client_type is used to indicate the client column. 0 is for the bundle, 1 is for the executable_path. The XCSSET malware is parasitic from the view of the bundle executable path, so how about the view of the bundle identifier, can I fake the bundle identifier as the legitimate application ?

Then I do a quick test, and the answer is yes ! 😎

  • Install Zoom.app, and grant Screen Recording permission to it.
  • Build a common Fake.app with the screen capture function.
  • Change the CFBundleIdentifier field to us.zoom.xos in the Info.plist of Fake.app.
  • Run the Fake.app, no user prompt while the screen capture is working.

Next, I will discuss how it works and how apple fixes it.

tccd internals

About tccd

tccd is a daemon process handling XPC requests with TCC message, its core logic is in the handle function to dispatch all kinds of TCC requests, including the operations to modify and query database. Of course, all the TCC request clients must have the necessary entitlements, such as com.apple.private.tcc.manager.

For example, when we grant the Screen Recording permission to Zoom.app in the System Preferences, the process com.apple.preference.security.remoteservice will send a XPC message like this :

image-20210723112548485

Note the client is the bundle identifier us.zoom.xos, and the granted is true.

Then open the Fake.app, the process universalAccessAuthWarn will send a message :

image-20210723113842257

Note that the granted is false, because this is an permission authentication operation.

The issue

The key point of this issue is in the function -[TCCDAccessIdentity initWithMessage:] and its caller function -[TCCDServer recordFromMessage:accessIdentity:error:] .

image-20210723175222133

From the pseudocode above, we can see: self->_path = [self->_bundle executablePath] and self->_bundle = [[self class] bundleForAppWithBundleIdentifier: self->_identifier]. Therefore, self->_path is determined by self->_identifier, which could be faked.

Actually, there are 2 applications sharing the same identifier :

image-20210724204230044

However, the API call [LSApplicationProxy applicationProxyForIdentifier:@"us.zoom.xos"] used here will only return the NSBundle </Applications/zoom.us.app> :

image-20210724203808671

One more issue

Theoretically, there is a csreq check to defeat the bundle identifier reusing attack. It should check the code signing requirement blob of the specified binary, details can be seen here.

However, as you can see from the first figure, the csreq column is NULL. This is because the code_requirement field of the XPC message is null-object, and then the function -[TCCDServer recordFromMessage:accessIdentity:error:] return a record item without the code signing requirement.

Apple Fix

Let’s check the key function first :

image-20210723175935595

The fix is adding a new field bundle_url for the XPC request message.

Request message for granting the permission :

image-20210723130717191

Request message for checking the permission :

image-20210723131028607

And in the function -[TCCDServer recordFromMessage:accessIdentity:error:] , if the code_requirement is null-object, it will call -[TCCDAccessIdentity designatedRequirementData] to get the default code signing requirement blob of the target binary.

image-20210726132641772

Conclusion

  • Thinking about the existing vulnerabilities from another angle, there may be new gains. 😃
  • Note that one bundle identifier could be mapped with many applications. So we shouldn’t use it to check for a specific application. Here tccd is the bad example, and I think the bundle identifier reusing attack could be applied to other scenarios. 🤔

Timeline

image-20210723132904052

Apple fixed the issue in the macOS Big Sur 11.5 (20G71), which was released on 2021-07-21.

Tips for the tccd debugging

If you debug the system tccd process on the local machine, it is easy to hang the operating system and you have to reboot your machine. This is because if the tccd process is interrupted into the debugger for a long time, then some processes related to UI will fail to get the XPC response from the system tccd process, and finally you cannot play your debugger again.

There are 2 ways for you :

  • If you like lldb debugger, then just ssh to the target machine, and launch lldb in the ssh shell.
  • If you prefer the IDA debugger, then just run the binary mac_server64 in the target machine, and use IDA Pro to connect remotely.
Written on July 24, 2021