Apple internals #10: The Objective-C runtime and the shared cache
An Objective-C object is one word you must decode before it means anything, and its framework is not a file on disk. Both change what an attacker controls.
An Objective-C object is one word you must decode before it means anything, and its framework is not a file on disk. Both change what an attacker controls.
Page-table writes and code-signing decisions left the kernel's privilege level. A kernel with arbitrary read/write must ask two monitors, SPTM and TXM, instead.
On arm64e the hardware signs the pointers worth hijacking and checks them on use. The key is out of reach of any memory write, so attacks work around it.
A memory-corruption bug is worth little until you decide what sits next to what you corrupted. Since iOS 15 the allocator decides that, not you.
One way out of a sandbox is a service doing exactly what it was built to do, for a caller it misidentified. Mach messages, MIG, XPC, and what decides it.
IOKit is the widest kernel surface a sandboxed iOS process can reach: hundreds of drivers, each vending a user client. Where the bugs live, plus CVE-2022-32832.
AMFI decides what may run; the sandbox decides what a process may touch. The profile is that list, and escaping is more often reading it than corrupting memory.
AMFI's rejection is the last line of a longer check: a MACF module and a verdict keyed on one 20-byte cdhash, from trust cache through CoreTrust to amfid.
XNU taken apart from an attacker's view: the Mach and BSD hybrid, the port-as-capability model the whole iOS security stack rests on, and what tfp0 actually is.
iOS runs nothing it hasn't verified. The boot chain walked in reverse, from the Boot ROM through iBoot and Image4 to the kernelcache, with a stop at checkm8.
The index for the series: ten posts from the SecureROM to objc_msgSend, what each layer of the iOS security stack decides, and which post covers which layer.
Rhysida derives every per-file AES key from a PRNG seeded with the encryption timestamp. Recover the timestamp and you regenerate every key.
One syscall reloads the whole CPU context from the stack. Forge the frame it reads and you control every register in one step. The technique we are named after.
The same primitive ladder works on almost every JavaScript engine: type confusion, addrof and fakeobj, arbitrary read/write, then RCE. On JavaScriptCore and V8.
From a default 16,704-byte hello world down to 73 bytes: stripping the toolchain layer by layer, removing sections, and overlapping the ELF and program headers.
Recording of my talk on Javascript engine exploitation methodology, given in French at Quarks in the Shell 2023 (Quarkslab).
Exploiting CVE-2011-4187, a stack buffer overflow in Novell iPrint Client's ActiveX component, from CVE ID to arbitrary code execution on Windows XP.
A packer hides a compressed payload in a PE's .rsrc section and unpacks it in memory at runtime. Reconstructed end to end, so you can pull the payload out.
Two ways to cross the Linux user/kernel boundary: a system call compiled into the kernel, and a module loaded at runtime. Both built, tested and contrasted.