“A Deep Dive into an NSO Zero-Click IMessage Exploit: Remote Code Execution”, 2021-12-15 (; backlinks; similar):
[Project Zero analysis of NSO Group’s FORCEDENTRY zero-click exploit of Apple iOS 13: a “GIF” is sent, which is decoded by the GIF-handling code as a PDF document containing highly-compressed JBIG2 images.
JBIG2 is an old but advanced image compression format, and has some memory bugs allowing a large block of RAM to be written to, providing the initial opening. But how to make that memory useful? It may contain random code, or data, or nothing at all.
The JBIG2 format allows defining variants of a little image by updating it with pixel-level boolean operations: negating, swapping, etc. So the attacker provides 70,000 of these image update commands, which collectively define an entire virtual machine which can do arithmetic etc; this virtual machine then runs an attacker-written program to scan RAM and do stuff.]
…As mentioned above, the substitution based compression output is lossy. After a round of compression and decompression the rendered output doesn’t look exactly like the input. But JBIG2 also supports lossless compression as well as an intermediate “less lossy” compression mode. It does this by also storing (and compressing) the difference between the substituted glyph and each original glyph…Rather than completely encoding the entire difference in one go, it can be done in steps, with each iteration using a logical operator (one of AND, OR, XOR or XNOR) to set, clear or flip bits. Each successive refinement step brings the rendered output closer to the original and this allows a level of control over the “lossiness” of the compression. The implementation of these refinement coding steps is very flexible and they are also able to “read” values already present on the output canvas.
…At this point [in the memory overflow exploit] it would also be possible to write to arbitrary absolute memory addresses if you knew their offsets from the page backing buffer. But how to compute those offsets? Thus far, this exploit has proceeded in a manner very similar to a “canonical” scripting language exploit which in Javascript might end up with an unbounded ArrayBuffer object with access to memory. But in those cases the attacker has the ability to run arbitrary Javascript which can obviously be used to compute offsets and perform arbitrary computations. How do you do that in a single-pass image parser?
My other compression format is Turing-complete! As mentioned earlier, the sequence of steps which implement JBIG2 refinement are very flexible. Refinement steps can reference both the output bitmap and any previously created segments, as well as render output to either the current page or a segment. By carefully crafting the context-dependent part of the refinement decompression, it’s possible to craft sequences of segments where only the refinement combination operators have any effect.
In practice this means it is possible to apply the AND, OR, XOR and XNOR logical operators between memory regions at arbitrary offsets from the current page’s JBIG2Bitmap backing buffer. And since that has been unbounded… it’s possible to perform those logical operations on memory at arbitrary out-of-bounds offsets…It’s when you take this to its most extreme form that things start to get really interesting. What if rather than operating on glyph-sized sub-rectangles you instead operated on single bits?
You can now provide as input a sequence of JBIG2 segment commands which implement a sequence of logical bit operations to apply to the page. And since the page buffer has been unbounded those bit operations can operate on arbitrary memory.
…Practical circuits: JBIG2 doesn’t have scripting capabilities, but when combined with a vulnerability, it does have the ability to emulate circuits of arbitrary logic gates operating on arbitrary memory. So why not just use that to build your own computer architecture and script that‽ That’s exactly what this exploit does. Using over 70,000 segment commands defining logical bit operations, they define a small computer architecture with features such as registers and a full 64-bit adder and comparator which they use to search memory and perform arithmetic operations. It’s not as fast as Javascript, but it’s fundamentally computationally equivalent.
The boot-strapping operations for the sandbox escape exploit are written to run on this logic circuit and the whole thing runs in this weird, emulated environment created out of a single decompression pass through a JBIG2 stream. It’s pretty incredible, and at the same time, pretty terrifying.