The exploit hiding in dead code: orphan-block DELEGATECALL
Some of the most dangerous bytecode never appears in the source. It lives in a basic block that looks unreachable — until a computed jump lands on it at runtime. Static source tools don't see it. Here's how BEVM finds it and Janus turns it into a kill chain.
Why source-level tools miss it
The EVM has no functions — only JUMP, JUMPI, and
JUMPDEST. A Solidity compiler emits a jump table at the top of the
contract that dispatches on the 4-byte selector. But jump targets can be
computed at runtime, and not every reachable JUMPDEST corresponds to
a public function in the source. When a block holds a powerful opcode —
DELEGATECALL, SELFDESTRUCT, an unguarded
SSTORE to slot 0 — but appears "orphaned" from the normal
dispatcher, a source-only linter has nothing to flag. The bytecode tells a
different story.
Step 1 — X-ray the bytecode
In BEVM you attach by address (or paste bytecode). Within a second you get the
disassembly, the resolved selectors, and the control-flow graph. The left rail
lists every basic block with a verb and a flag — and the ones we care about are
tagged orphan:
# BEVM · CFG BLOCKS
0x2f2 PROXY PATCH → JUMPI (46) SSTORE orphan / unreached
0x32b PROXY TRANSFER → JUMPI (36) CALL orphan / unreached
0x4a1 OPAQUE → JUMP (12) DELEGATECALL orphan / unreached
Block 0x4a1 contains a DELEGATECALL and is marked
orphan / unreached — the standard dispatcher never falls through to
it. That is precisely the kind of block an attacker hunts for.
Step 2 — can it actually be reached?
"Unreached by the dispatcher" is not the same as "unreachable." BEVM builds a
reachability map and looks for computed jumps — a
JUMP whose destination is taken from calldata, storage, or a prior
computation. If some path lets an attacker steer the program counter onto
0x4a1, the dead code is suddenly very much alive.
# attack-graph: who can write the jump target?
selector 0x5ec2dc8d → writes slot 0x6 (jumpTarget) ungated
block 0x4a1 ← JUMP [slot 0x6] DELEGATECALL [slot 0x7]
slot 0x7 (impl) ← writable via selector 0x3d103b6d ungated
Now the shape is clear: two ungated setters let an attacker control both the jump
target and the DELEGATECALL implementation address. Point
the jump at 0x4a1, point the implementation at an attacker contract,
and the victim delegate-calls into attacker code with the victim's storage and
balance. That is full takeover.
Step 3 — prove it on a fork
A graph is a hypothesis. BEVM's Intruder seeds the GPU fuzzer with the attack-graph routes (orphan-block PCs get priority), and any candidate that moves value is replayed on an Anvil mainnet fork. Only a strictly positive, attacker-controlled balance delta is reported:
# BEVM · fork replay verdict
chain: setJumpTarget(0x4a1) → setImpl(attacker) → trigger()
step 1 ok SSTORE slot 0x6 = 0x4a1
step 2 ok SSTORE slot 0x7 = 0xATTACKER
step 3 ok DELEGATECALL → attacker.sweep()
─────────────────────────────────────────────
CONFIRMED attacker_eth_delta = +142.3 ETH (replayed on fork)
Step 4 — let Janus explain it
Findings still need to be understood and fixed. Paste the result into Janus and it reasons like an attacker, then writes it up for a human:
CRITICAL — Arbitrary DELEGATECALL via computed jump. Two ungated setters (0x5ec2dc8d,0x3d103b6d) let any caller control the runtime jump target and the delegatecall implementation. An attacker steers execution into orphan block0x4a1and delegatecalls attacker-owned code, executing with the contract's storage and funds — equivalent to full ownership. Fix: gate both setters behind access control, and prefer a fixed jump table over storage-derived targets; never delegatecall to an address taken from unauthenticated state.
Why the combination matters
No single technique gets here. Static analysis flags the opcode but can't tell you it's reachable. A fuzzer without the CFG wastes its budget on the front door. Reachability + computed-jump discovery finds the path; GPU fuzzing + fork replay proves it; and Janus turns the proof into something a team can act on. That's the BEVM + Janus loop: find, prove, explain.
Illustrative example for education. Addresses, blocks, and the ETH figure are synthetic. Only run security testing against contracts you own or are explicitly authorized to test.