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.
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.
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.
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 on
NSPredicate 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
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.
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.