FORCEDENTRY: Sandbox Escape

We discussed this vulnerability as part of our weekly podcast on 06 April 2022

Follow-up to the December post which covered an int overflow in the CoreGraphics PDF parser for the JBIG2 image format, which implemented a weird machine / mini architecture to execute code. This post covers the sandbox escape that was chained with it, which unlike the first bug, is a logic issue rather than a memory corruption.

Background A bit of background is necessary to understand the sandbox escape, because it’s a very intricate and complex bug. First, objects in obj-c have a metadata field called the isa field, which stores a pointer to the object’s class along with other stuff. Prior to iOS 14.5, this field was not protected by Pointer Authentication Codes (PAC). This allowed the exploit authors to fake different types of obj-c objects.

One critical type of object for this exploit is the NSPredicate object, which can be used to query and filter other objects such as NSArrays. These predicates can be created in the form of expressions through keywords, such as NSFunctionExpressions with the “FUNCTION” keyword to invoke an arbitrary method. With some tricks, this can be effectively be used to execute arbitrary obj-c code.

Sandbox escape Once arbitrary code can be ran, the sandbox escape comes into play via attacking a service over NSXPC (obj-c’s RPC mechanism). Basically with NSXPC, a service can export an object with a protocol attached which defines which methods and argument types can be used by the client. What’s noteworthy is, the type enforcement is pretty loose by design. Not only will that type be permitted and deserialized by the service, but any sub-class of that type will also be accepted and deserialized. In an extreme case, any protocol that accepts an NSObject somewhere can effectively receive almost any object, since most objects inherit from it.

The evaluateMobileSubscriberIdentity() method of the CoreTelephonyClient for the CommCenter service accepted NSObject types in it’s protocol. In theory, an attacker can pass an NSPredicate object here, which when deserialized will execute arbitrary obj-c in the CommCenter service context. In practice, there’s some defense in depth onNSPredicate objects where it won’t be evaluated until allowEvaluation() is called. Since evaluateMobileSubscriberIdentity() doesn’t expect predicates, this will never get called.

Fortunately for the attackers, there’s another object from the PrototypeTools framework called PTSection, which has a child NSArray of PTRow objects. These PTRow objects have conditions, which can be evaluated as predicates. By passing a PTSection with a PTRow that has a malicious NSPredicate, code execution in the CommCenter service can be achieved when the object is deserialized.

Fix The primary fix was introduced by Apple in iOS 15.1. They now forbid NSExpressions from performing operations with significant side-effects like creation and destruction of objects, neutering the ability to execute arbitrary obj-c.