The State of iOS Jailbreaking in 2025
The State of iOS Jailbreaking in 2025
Header
LC_SEGMENT_64
LC_SEGMENT_64 Code signature
LC_SEGMENT_64
LC_SEGMENT_64 CSSLOT_CODEDIRECTORY
(…) CSSLOT_ALTERNATE_CODEDIRECTORIES
CSSLOT_ENTITLEMENTS
LC_CODE_SIGNATURE CSSLOT_DER_ENTITLEMENTS
CSSLOT_REQUIREMENTS
CSSLOT_SIGNATURESLOT
Code directories
CSSLOT_CODEDIRECTORY & CSSLOT_ALTERNATE_CODEDIRECTORIES
https://github.com/apple-oss-distributions/xnu/blob/main/EXTERNAL_HEADERS/CoreTrust/CTEvaluate.h
Code signature validation
CoreTrust
• Policy ags are set based on the certi cate’s common name and public key
• Chains without an Apple root will have policy ags set to zero
• AMFI will subsequently reject the binary
• CoreTrust does not verify that both CD hashes are correct
• Purely used to validate the CMS signature itself
fl
fi
fl
Code signature validation
am d
• Userspace daemon that checks developer and enterprise certi cates for AMFI
• Also performs checks on TestFlight apps
• Has been intercepted by attackers in the past to allow any code signature
• Attaching an expired certi cate to every binary causes am d to validate all of them
• Can hook MISValidateSignature(AndCopyInfo) to force validation to succeed for every binary
• Hardly used in a regular iOS user scenario
• Average user is unlikely to install developer/enterprise/TestFlight applications
• Why do App Store apps never reach am d?
fi
fi
fi
fi
fi
App Store Fast Path
Overview
• The App Store Fast Path makes CoreTrust an attractive target for an attacker
• Trick CoreTrust into setting the App Store policy ag, bypass codesigning
• Logic bugs are always 100% reliable in exploitation
• Multiple interesting uses
• Permanently signed applications
• Arbitrary entitlements
• Spawning binaries as root (or any UID, for that matter)
• Persistence on iOS 14, by replacing a system binary (e.g. launchd)
fl
CoreTrust bypasses
• Since iOS 14, there have been two public CoreTrust bypasses
• Both used in TrollStore and di erent points
• No incentive for such logic bugs in iOS 12 and 13
• One found in-the-wild as part of an €8m spyware chain
• Largely unmitigated between the rst and second bypasses releasing
• Have been mitigated since (more details on this later on)
ff
fi
CVE-2022-26766
The rst bypass
• Contrastingly to the rst bypass, one of the most complex bugs we have seen
• Made use of multiple ‘quirks’ in CoreTrust to bypass validation
• As mentioned earlier, a CMS signature can have multiple signers
• Question: did CoreTrust handle this case correctly?
• Same error variable used for each signer (if the last one was valid, they all were)
• The CD hashes returned to AMFI were collected from the rst signer (us)
• The policy ags were always set based on the second signer (App Store)
• By creating a crafted code signature with a fake App Store signer, you could bypass CoreTrust
fl
fi
fi
CVE-2023-41991
Entitlements
• SHA-256 code directory containing the correct hashes for our binary
• All hashes are correct and will be validated by the kernel
• Does not match the second CD hash in the App Store signer’s signature blob
• Matches the second CD hash in our custom signer’s signature blob
• Meaning our custom signer contains two ‘correct’ CD hashes
CVE-2023-41991
Signature blob
• By making installd’s preferred slice a normal App Store binary, and the kernel’s
preferred slice TrollStore Helper, you could sideload or install the app via Safari
• On versions without a public PPLRW chain (iOS 16.6.x), it uses kernel exploit
primitives to install a persistence helper into a system app by abusing vnodes
• User can then open said app and install TrollStore via that
TrollRestore
iOS 15.2 - 17.0
• Uses CVE-2024-44252, a vulnerability in the backup system that allows restoring les to arbitrary
le paths
• Jailbreak for
• iOS 15.0 - 16.5 arm64e
• iOS 15.0 - 16.6.1 arm64
• Uses kernel exploit + PPL bypass to gain PPL R/W primitives
• dlopen(„/some/library.dylib“)
• Dyld parses library and nds code signature
• Attaches signature to the le descriptor
• fcntl(fd, F_ADDFILESIGS, <code signature o set/size>)
• Then maps the executable segment via mmap()
• Kernel veri es that attached code signature is valid
fi
fi
fi
ff
Code Signature Validation
Valid under what conditions?
fi
fi
Code Signature Validation
TrustCache Linked List
pmap_image4_trust_caches
CDHash(„/System/Library/
Caches/com.apple.dyld/
CDHash(„/usr/lib/dyld“) dyld_shared_cache“)
CDHash(„/sbin/launchd“) CDHash(„/System/Library/
Caches/com.apple.dyld/
<…> dyld_shared_cache.1“)
<…>
Code Signature Validation Bypass
TrustCache Injection using PPL R/W
pmap_image4_trust_caches
CDHash(„/System/Library/
Caches/com.apple.dyld/ CDHash(„/var/jb/basebin/
CDHash(„/usr/lib/dyld“) dyld_shared_cache“) launchdhook.dylib“)
• Hook fcntl in dyld to automatically add any library that’s about to me mapped
to TrustCache
• Idea: Server with PPL R/W that the entire system can talk to via IPC
• Victim: launchd, accessible system wide
• Server can then
• Add CDHashes to TrustCache
• Provide any additional functionality utilizing the primitives that we need for
the jailbreak
Jailbreaking: Step by Step
Page Table
Userspace
Pages
Jailbreaking: Step by Step
Page Table
Userspace Kernelspace
Pages Pages
Jailbreaking: Step by Step
pmap_image4_trust_caches
BaseBin • Use PPL R/W to upload
Stock basebin.tc into Kernel
TrustCache memory
basebin.tc
Jailbreak
• Add to linked list of
BaseBin TrustCaches
TrustCache
Dopamine.app PPL R/W • Allows BaseBin binaries
to execute
initports[2] launchd
• Host mach server inside
Dopamine
Mach
• Use opainject to inject
Server
Dopamine.app PPL R/W launchdhook.dylib into
launchd
Page Table
Userspace Kernelspace
Pages Pages
Jailbreaking: Step by Step
Process
Pages Pages
• Add logic to process jailbreak
related messages, utilizing it’s
PPL R/W capabilities
fi
Jailbreaking: Step by Step
Mach
Dopamine.app PPL R/W
Server
Page Table
Userspace Kernelspace
Pages Pages
Jailbreaking: Step by Step
dyldhook.merge.dylib MachOMerger
Mach
Dopamine.app PPL R/W
Server
Page Table
Userspace Kernelspace
Pages Pages
Patching the Dynamic Linker (dyld)
• Can only use library functions already in dyld, need to reimplement a lot
• We can hijack dyld_start to get code execution right when the process starts
• We can hook any function we want by replacing it with a trampoline to our
code
• When mapping a library, dyld will call the fcntl syscall with F_ADDFILESIGS
and pass the code signature o set and size to that
• This makes the Kernel attach a code signature, which allows a later mmap()
call to succeed
• To fully bypass library validation, we can hook fcntl to automatically add the
code signature to TrustCache by sending it to the jailbreak server
ff
dyld Patch #3
Entry Point
launchd
• Redirect the entry point of dyld to our own code
launchdhook.dylib • Send „check-in“ message to jailbreak server in
launchd
dyld
Process
Process Patch #1
Bypassing Page Validation
• This makes the system think the process is debugged, which makes it allow
writing unsigned executable code
fi
Process Patch #2
Bypassing Sandbox Restrictions
• We want to weaken the sandbox so that any process will be able to access
the jailbreak les at „/var/jb“
• Read-access to „/var/jb“
• Read-write-access to „/var/jb/var/mobile“
• Reimplement various syscalls in dyldhook to consume sandbox extension in
the local process, granting the permissions we want
fi
Jailbreaking: Step by Step
mount
BaseBin .fakelib /usr/lib/ Filesystem • Mount /var/jb/basebin/.fakelib
on top of /usr/lib
jbctl
• Using bindfs mount
• Requires
„com.apple.private.bindfs-
Mach
Server
Dopamine.app PPL R/W allow“ entitlement
• Normally prohibited by
sandbox, need to steal kernel
Page Table
credentials in jbctl (omitted
Userspace Kernelspace for simplicity)
Pages Pages
Systemwide Hook
Code Signature Validation Bypass
• Code signing is now fully bypassed for any new processes that spawn
• What about the old processes?
• We need to reboot the userspace!
• During a userspace reboot, launchd will call execve on itself
• We need to nd a way to preserve the jailbreak through that
• Then all we need to do is reboot the userspace and the device will be fully
jailbroken
fi
Userspace Reboot: Step by Step
Process
• Intercept posix_spawn call right before
re-exec by hooking it
Userspace Reboot: Step by Step
Page Table
• Boomerang tells temporary
initports[2] boomerang PPL R/W
server to transfer the PPL R/W
primitive and other important
stu to it’s process
Hook
• Server uses PPL R/W to add
kernelspace pages to page
Server launchd launchdhook.dylib table of boomerang
PPL R/W
• Boomerang now stores
Page Table primitives to persist them
across userspace reboot
Userspace Kernelspace
Pages Pages
ff
Userspace Reboot: Step by Step
Userspace Kernelspace
Pages Pages
Page Table
• Boomerang creates temporary
Server boomerang PPL R/W
server
Page Table
PPL R/W
• launchd triggers re-exec
Userspace Kernelspace
Pages Pages
Userspace Reboot: Step by Step
Userspace Kernelspace
Pages Pages
Page Table
• launchd is reexecuted with
Server boomerang PPL R/W
launchdhook.dylib reinjected
• Connection to boomerang in
initports[2] launchd launchdhook.dylib initports is retained
Userspace
Pages
Userspace Reboot: Step by Step
Userspace Kernelspace
Pages Pages
Page Table
• launchdhook.dylib tells
Server boomerang PPL R/W
boomerang server to transfer
PPL R/W and other related
primitives / o sets back to it
Userspace Kernelspace
• The entire userspace is restarted with the
Pages Pages jailbreak patches in place
Summary
• Jailbreak apps are added to icon cache by additional launch daemon added
by the jailbreak
• Pro t???
fi
fi
Final result: semi untethered
jailbreak for iOS 15 and 16
The End
Questions?
(We probably don’t have time for those)