every so often a talk reorganises how i think about something. Dion Blazakis’s XNU case study did that to me. he relearned a kernel he hadn’t seriously touched since 2010 and built a local privilege escalation in about five days, with an LLM doing a big chunk of the grunt work.
none of this exploit is mine. it’s Dion’s, all of it, from his [un]prompted.au talk and two public Apple CVEs. i just pulled on it, because i keep going back and forth on where AI is actually useful in vuln research and where it’s just a demo.
no hype. i promise.
EXPLOIT-DEV-AS-GRAPH-SEARCH
Dion’s talk frames an exploit as a path through a graph. that’s the part that stuck with me.
you start with one weak primitive. a type confusion, say. on its own it does almost nothing. the whole job of exploitation is pushing that primitive through a graph of intermediate capabilities until you reach the goal node, which is usually code execution or root. leak. arbitrary read. arbitrary write. pc control. each one is a node. you prove a node is reachable by writing a PoC that makes the jump from the last one. no PoC, no hop.
Dion says it better than i will: exploit development is the process of mapping a path through the attacker capability graph.
once you see it that way, the history of exploitation is just a difficulty ladder measured in hops (jumping from one node to the next, if you will):
- the Morris Worm, 1988. one hop. a
fingerdstack overflow goes straight to RCE. node to node, done. - Pwn2Own iPhone 2011. several hops, all by hand. heap overflow, then grooming, then ROP, then pc control. Dion pulled this one off himself back in 2011, with Charlie Miller. so the “before AI” baseline here is just him fifteen years younger, grinding it out by hand.
- QuickJS, via Sean Heelan’s Anamnesis project in 2026. UAF, leak libc and binary base, arbitrary r/w, shell. and here the LLM agents do the traversal unassisted.
so the interesting claim isn’t “AI writes exploits”. it’s that graph traversal is exactly what agents turn out to be not terrible at. you give the agent a task that roughly looks like graphs (find any edge that gets me closer to the goal) instead of the basics “write me an exploit” prompt, and it goes and explores. the human stops writing the exploit and starts picking which node to hop to next. i deeply suspect that when people say they prompt their LLM better, or whatever the fuck, they mean that:
- they’re able to decompose a problem and think about it in graphs,
- they have enough technical background that they understand the LLMs’ nonsense.
THE-CHAIN, HOP BY HOP
okay. here’s the chain, according to the talk.
the type confusion (CVE-2026-64704)
the entry primitive lives in smbfs.kext. when it handles an SMB2_CREATE request that uses SMB2_CREATE_AAPL_RESOLVE_ID, a file_id that came back from fsgetpath() gets reused and misread as a struct smb2_lease *.
that gives you a write into kernel memory, but a constrained one. the talk calls it a “messy write”: a couple of fields you control, a couple of fields forced to zero, and the whole thing only fires if adjacent fields already hold the right values. patched 2026-07-27, CVSS 9.8.
a write that constrained is a long way from the clean write-what-where you actually want, so a big chunk of the work later goes into taming it into something you can point where you need it.
the leak (CVE-2026-64699)
separate bug, same day’s patch. webdav.kext’s read path calls kalloc_large(N). when the server response comes back short (say M bytes, with M < N), the kext hands back the full N-byte buffer anyway. so you get N - M bytes of whatever used to live in that allocation. stale heap residue, straight out of the kernel. CVSS 5.5.
on its own, boring. as a hop in the chain, gold. because now you have a leak primitive, and now perhaps you can defeat kASLR.
beating kASLR
the idea is spray, free, reclaim. you fill the heap with an allocation that’s packed with live kernel pointers, free it, then make the vulnerable webdav read land on that exact freed memory. the kalloc_large buffer webdav hands you back is now full of stale residue, and that residue still holds a real kernel pointer. read it back and kASLR is done.
the only open question is what you spray. first they tried Mach IPC port-pointer arrays, big kernel allocations packed with pointers. MTE’s tag checking faulted on those, so that was out. instead the agent had spray something dumber, a few thousand ICMPv6 raw sockets. same trick, and this time it worked.
turning a leak into arbitrary read/write
this is the prettiest hop. the in6p_icmp6filt field on a socket is just a pointer to a little filter struct, and two normal syscalls act on it: getsockopt reads from wherever it points, setsockopt writes there. so whoever controls that pointer controls what those two syscalls read and write.
you line up two ICMPv6 sockets so the first socket’s filter pointer lands on the second socket’s filter pointer. now a setsockopt on the first socket overwrites the second’s pointer, so you can keep re-aiming the second socket at any address you want and then read or write it with an ordinary syscall. a syscall you’re allowed to call, pointed at memory you’re not allowed to touch, and reusable because you can re-aim it as many times as you like. this replaced an earlier __DATA-segment overwrite attempt that was unstable.
privesc without touching the cred
you’d think the end is “overwrite the credential struct, become root”, but XNU hardened it. the credential and the structs that point at it now live in read-only kernel zones (Apple wrote up how that works), so even an arbitrary write doesn’t reach them. direct forgery of the cred is off the table.
so the chain does something sneakier with the arbitrary r/w it just built. it leaves a worker thread sitting inside its own PoC process, stuck mid-syscall on a pipe read. while that thread is asleep, it swaps the thread’s virtual-memory map pointer to point at launchd’s address space. now reads and writes through that thread hit root-owned memory directly. then it swaps the map back before the thread wakes up.
steal a thread, swap its map, do your business, put it back before anyone notices. no cred forgery anywhere.
MTE breaks the first primitive
MTE killed the first read/write primitive. a pointer-tag mismatch faulted and the whole approach had to change. here, Dion had to go “fine, different primitive then”. i’m glad the talk shows the difficult part instead of pretending the chain went smoothly. too often, hype pieces lead us to believe all you have to do is say “mr. llm, please hack this” and it works. the reality is more complicated.
the rig
for the people who are going to skip straight here anyway. the setup Dion describes:
ipswand anipswskill for pulling apart Apple firmware- IDA Pro with an MCP bridge, so the agent can actually drive the disassembler
- a Virtualization.framework VM with
lldbdoing kernel debugging - a dedicated machine on its own network, SSH in, passwordless sudo
- a local checkout of Apple’s open-source XNU
- and the agent prompts pointed at a folder of other unverified candidate bugs to go try
the agent compiles things, it debugs things, it proves a hop by running a PoC. an agent that can only talk about exploitation is useless. an agent hooked up to a kernel debugger and a disassembler is somewhat useful.
so can i fire my brain ?
well in my opinion, no.
i’ve looked at a fair few of these “point an LLM at a target and let it cook” setups now. the good public ones, Big Sleep and Quarkslab’s FreeRDP workflow, are very good pieces of engineering, that you should use as inspiration. but, most of what i run into isn’t that careful. it’s the same two stages every time: static analysis finds candidate bugs, then an agent tries to confirm them on a live target. that kind of works, but it’s got the same few problems almost every time.
first, pair-hacking still beats the autonomous run. in the talk, every real win came from a human stepping in. he kept redirecting the agent: why not go straight from X to Y instead of that ugly pivot, why are you grooming when you already have a leak. the agent is good at exploring things, but not at picking the right node to attack on its own (not reliably anyway). take the human out and the quality falls off a cliff.
second. confirmed reachable is different from exploited, but you won’t be told the difference in those hype-pieces. a lot of pipelines stop the second they prove a bug is reachable on a live box. that’s a real result. it’s also not a chain. proving the type confusion fires is a different planet from the launchd map-swap at the end. if your pipeline emits vulnerable and there’s no exploit behind it, say so, because the reader hears popped.
to be fair, this is a problem with the industry in general. findings tend to be over-hyped to generate interest sometimes. over time, it puts flawed expectations upon researchers, but that’s a topic for another time.
then there’s the money. one expensive run per finding when the targets are near-identical. you pay over and over to confirm the same bug. cluster and dedup first, then run the expensive step on what’s left. tokens are not cheap, people.
finally, the runs are black boxes. no in-run checkpoints, no checks on the hard sub-steps, or a trace you get to read after it’s already dead. compare that to the Quarkslab FreeRDP work, which wraps the very hard steps (libc base recovery from a leak, payload composition) in measurable checks with a human intervention point. that’s the standard. most setups aren’t close.
exploitation with an agent feels more like theorem proving than writing code. the agent searches for a path, and now and then it finds a hop you missed. it’s not writing a program front to back. that’s why the tooling matters so much: give it a debugger, let it prove one hop at a time, and keep a human choosing where to go next.
see you next time :)~
links
- Dion Blazakis, “AI-Assisted Exploit Development: An XNU Case Study”, the talk this whole post is about
- CVE-2026-64704, the smbfs type confusion, the entry bug
- CVE-2026-64699, the webdav heap-residue leak
- Apple on XNU read-only allocators, why you can’t just overwrite the cred anymore
- Spafford, “The Internet Worm Program: An Analysis”, the 1988 Morris worm writeup
- Pwn2Own 2011: iPhone and BlackBerry fall, the Miller/Blazakis iPhone hack (winners are gagged, so no technical writeup exists)
- Sean Heelan, Anamnesis, LLM agents writing real QuickJS exploit chains unassisted
- Google Project Zero, Big Sleep, the obvious “point it at a target” agent
- Quarkslab, “From AI Agents to RCE”, the most worked-out staged pipeline i’ve seen
- ipsw and its skill, for pulling apart Apple firmware
- IDA Pro + ida-pro-mcp, the disassembler and an MCP bridge for it
- Virtualization.framework and
lldb, the VM and kernel debugger - Apple’s open-source XNU, the kernel source
- background: ROP, kASLR, MTE, use-after-free, launchd