An Objective-C object is one word you have to decode before it means anything, and the framework it belongs to is not a file on the disk. Both are readable, and both change what an attacker has to control.
Page-table writes and code-signing decisions moved out of the kernel's privilege level. A kernel with arbitrary read and write has to ask two monitors instead, and memory tagging depends on them.
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 the attacks work around it instead.
A memory-corruption bug is worth almost nothing until you decide what sits next to the thing you corrupted. Since iOS 15 the allocator decides that for you, so exploits moved to physical pages instead.
One way out of a sandbox is a service doing exactly what it was built to do, for a caller it has misidentified. Mach messages, MIG, XPC, and the three functions that decide it.
IOKit is the widest kernel surface a sandboxed iOS process can reach: hundreds of drivers, each vending a user client you open and call. Where the bugs live, and CVE-2022-32832 walked end to end.
AMFI decides what may run; the sandbox decides what a running process may touch. The profile is the exact list of what it can reach, and escaping is more often reading that list than corrupting memory.
'AMFI rejected your binary' is the last line of a longer check. Behind it: one MACF policy module and a verdict pipeline keyed on one 20-byte value, the 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. A reverse-engineering walkthrough and a minimal decryptor.
Adam Taguirov·14 minransomwarereverse-engineeringcryptographymalware-analysis
One syscall reloads the entire CPU context from the stack at once. Forge the frame it reads and you control every register in a single step. The technique we are named after, built from the ground up.
The same primitive ladder works on almost every JavaScript engine: a memory bug to type confusion, addrof and fakeobj, arbitrary read/write, then code execution. Built on JavaScriptCore and V8.
Adam Taguirov·12 minbrowserjavascriptexploitationwebkit
From a default 16,704-byte hello world down to 80 bytes: stripping the toolchain layer by layer, removing sections, and overlapping the ELF and program headers.
Reverse-engineering and 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.
Adam Taguirov·16 mincvereverse-engineeringexploitationwindows
A packer hides a compressed payload inside a PE's .rsrc section and unpacks it in memory at runtime. We reconstruct the technique end to end so we can recognise it and pull the payload back out during analysis.
Two ways to extend the Linux kernel and cross the user/kernel boundary: a static system call compiled into the kernel, and a dynamic module loaded at runtime. We build, test, and contrast both end to end.
Adam Taguirov·17 minlinuxkernelsyscallkernel-module