Since the first version of iOS on the original iPhone, Apple has enforced careful restrictions on the software that can run on their mobile devices. Only applications that were cryptographically signed by a developer certificate trusted by Apple could be executed, and scripting languages like AppleScript were not found on iOS. The ability to dynamically execute code was nearly completely removed, creating a powerful barrier for exploits which would need to find a way around these mitigations to run a malicious program. As macOS has continually adopted more features of iOS it has also come to enforce code signing more strictly. The Trellix Advanced Research Center vulnerability team has discovered a large new class of bugs that allow bypassing code signing to execute arbitrary code in the context of several platform applications, leading to escalation of privileges and sandbox escape on both macOS and iOS. The vulnerabilities range from medium to high severity with CVSS scores between 5.1 and 7.1. These issues could be used by malicious applications and exploits to gain access to sensitive information such as a user’s messages, location data, call history, and photos.
Finding a new bug class
In September 2021 Citizen Lab revealed FORCEDENTRY, a 0-click iOS remote code execution exploit that targeted a Saudi activist in order to infect their iPhone with the Pegasus Malware. They further collaborated with Google Project Zero to analyze the exploits, leading to a pair of posts detailing the methods used in the attack. Part 1 described the initial exploitation of PDF parsing code and Part 2 laid out the sandbox escape. While much attention was given to the first exploit, we were much more interested in the second as it described a way to dynamically execute arbitrary code in another process which completely sidestepped code signing. It involved NSPredicate, an innocent looking class that allows developers to filter lists of arbitrary objects. In reality the syntax of NSPredicate is a full scripting language. The ability to dynamically generate and run code on iOS had been an official feature this whole time. However, this was just the beginning, as this feature revealed an entirely new bug class that completely breaks inter-process security in macOS and iOS.
The FORCEDENTRY Sandbox Escape was not the first exploration of this behavior. CodeColorist, a prolific iOS userspace security researcher, designed a challenge for Real World CTF 2019 that involved exploiting the mechanics of NSPredicate. He subsequently gave a talk and published the blog post See No Eval in January 2021 which detailed how to use them to run arbitrary code. The gist of this research was that NSExpression objects, the building blocks of an NSPredicate, could be used to call arbitrary methods on arbitrary classes and objects. Using existing classes in Apple’s private frameworks, it was possible to bypass pointer authentication (PAC) and every other mitigation to call any function. However, the post also describes ways in which Apple has mitigated the dangerousness of these objects, namely through a protocol called NSPredicateVisitor. Classes that implement this protocol can be used to check every expression to make sure they were safe to evaluate. CodeColorist notes at the end of his post that „Without a proper validation, it could be an inter-process attack surface to bypass TCC.“
The See No Eval post seemed to have been the main idea behind the sandbox escape used in FORCEDENTRY. Since then, Apple has removed features used in this exploit and CodeColorist’s blog post and added new mitigations to attempt to restrict what could be done with NSPredicate. These mitigations used large denylist to prevent the use of certain classes and methods that could clearly jeopardize security. However, we discovered that these new mitigations could be bypassed. By using methods that had not been restricted it was possible to empty these lists, enabling all the same methods that had been available before. This bypass was assigned CVE-2023-23530 by Apple. Even more significantly we discovered that nearly every implementation of NSPredicateVisitor could be bypassed. There are many processes with XPC Services (the primary method of high-level inter-process communication on macOS and iOS) that accept NSPredicate arguments and use NSPredicateVisitor to ensure that the provided expression is safe to evaluate. While there is no single implementation as nearly every process has its own version, most use the “expressionType” property to filter out function expressions. The issues reside in the fact that this property can be set in the sending process and is trusted to be accurate by the receiver, rendering the checks useless. This bypass was assigned CVE-2023-23531. These two techniques opened a huge range of potential vulnerabilities that we are still exploring.
The first vulnerability we found within this new class of bugs is in coreduetd, a process that collects data about behavior on the device. An attacker with code execution in a process with the proper entitlements, such as Messages or Safari, can send a malicious NSPredicate and execute code with the privileges of this process. This process runs as root on macOS and gives the attacker access to the user’s calendar, address book, and photos. A very similar issue with the same impact also affects contextstored, a process related to CoreDuet. This result is similar to that of FORCEDENTRY, where the attacker can use a vulnerable XPC service to execute code from a process with more access to the device.
The appstored (and appstoreagent on macOS) daemons also possess vulnerable XPC Services. An attacker with control over a process that can communicate with these daemons could exploit these vulnerabilities to gain the ability to install arbitrary applications, potentially even including system apps.
There were also vulnerabilities of this class in services that could be accessed by any app, with no entitlements necessary. The first of these we found was in OSLogService, an XPC service that can be used to read potentially sensitive information from the syslog. More significantly an attacker can exploit an NSPredicate vulnerability in UIKitCore on the iPad. By setting malicious scene activation rules an app can achieve code execution inside of SpringBoard, a highly privileged app that can access location data, the camera and microphone, call history, photos, and other sensitive data, as well as wipe the device.
The vulnerabilities above represent a significant breach of the security model of macOS and iOS which relies on individual applications having fine-grained access to the subset of resources they need and querying higher privileged services to get anything else. Services that accept NSPredicate arguments and check them with insufficient NSPredicateVisitors allow malicious applications and exploit code to defeat process isolation and directly access far more resources than should be allowed. These issues were addressed with macOS 13.2 and iOS 16.3. We would like to thank Apple for working quickly with Trellix to fix these issues.