Description
A very fast program. Find the vulnerability and exploit it to read the flag.
Setup
Download the V8 build and the challenge patch/diff.
wget https://mercury.picoctf.net/static/.../d8wget https://mercury.picoctf.net/static/.../turboflan.diffchmod +x d8Solution
Walk me through it- Step 1Read the V8 patch and identify the typer bugApply the diff against the matching V8 commit and read it carefully. The bug is almost always a handful of lines in src/compiler/typer.cc, src/compiler/operation-typer.cc, or a new builtin that lies about its return range.bash
less turboflan.diffbash# Look for: return Type::Range(min, max, zone) with wrong boundsbash# Or a new builtin marked kNoWrite that actually mutates stateLearn more
V8 Turbofan, not C heap."Turboflan" is a pun on Turbofan, V8's optimizing JIT compiler for JavaScript. The vulnerability lives in JS-level reasoning, not in glibc heap structures.
Typer bug families that show up in CTF:
- Wrong range: a typer rule reports a tighter range than reality. Turbofan elides
CheckBoundsbased on the false range, then at runtime the value escapes the range and reads/writes past the array. - Missing side-effect annotation: a function is marked pure when it isn't. Turbofan reorders side-effecting code across it, breaking a check.
- NaN / -0 confusion: the typer drops a special-value case and the JIT-compiled code mis-handles it.
- Speculation guard skipped: a bounds check is removed because the typer claimed the index was already in range.
See real-world bug patterns for adjacent JS sandbox-escape ideas.
- Wrong range: a typer rule reports a tighter range than reality. Turbofan elides
- Step 2Trigger JIT compilation and force the bugStandard recipe: write a small attack function, force optimization via repeated calls (or %OptimizeFunctionOnNextCall in d8 with --allow-natives-syntax), then call once with an input the typer mispredicts.bash
# Run d8 with native syntax to force optimization: ./d8 --allow-natives-syntax exploit.jsbash# Sketch of a typer-confusion exploit (specifics depend on the diff): # function leak(x) { return arr[x]; } # for (let i = 0; i < 0x10000; i++) leak(0); # %OptimizeFunctionOnNextCall(leak); # leak(badValue); // typer thinks in-range, runtime is out-of-range -> OOB readLearn more
From OOB read to flag. A typical chain: corrupt a JSArray's elements pointer (or its length) to fabricate a fake array that addresses arbitrary memory; build addrOf/read/write primitives; locate
%FunctionPrototype%code object; flip a writable but executable page; drop shellcode; call.readFile("flag.txt")in d8 is often available too, sidestepping shellcode entirely.
Flag
picoCTF{...}
Turboflan is a V8 Turbofan JIT typer-confusion challenge, not a glibc heap bug. The published walkthroughs from this round are sparse and depend on the exact diff shipped, so treat this writeup as the framing rather than a turn-key exploit.