Reproduced Exploit

Taiko — block proposal spends a prover's allowance and takes 1000 TKO

proposeBlock stores the caller-supplied coinbase unless it is the zero address. onBlockProposed pays the assigned prover with transferFrom(coinbase, prover, fee). An honest prover has approved the hook for type(uint256).max and holds 1000 TKO. A proposer sets coinbase to that prover, the fee to 100…

Feb 2024•Other•access-control•3 min read

Chain

Other

Category

access-control

Date

Feb 2024

Source

AuditVault

EVM Playground

Source-level debugger — step opcodes and Solidity in sync

evm-hack-analyzer

The attack is replayed in an in-browser EVM preloaded with the exact dumped fork state. The execution tree shows every call; step by Solidity line or by opcode across all depths — source, Stack, Memory, Storage, Balances (native / ERC-20 / NFT), Transient storage and Return value stay in sync. Click a tree node, opcode, or source line to jump. No backend, no live RPC.

Loading fork state…

Source & credit. Reproduction of a public audit finding curated by AuditVault — the original finding: 34318-vulnerability-in-block-proposal-function-allows-token-theft. Standalone Foundry PoC and full write-up: 34318-vulnerability-in-block-proposal-function-allows-token-theft_exp in the evm-hack-registry mirror.


Vulnerability classes: vuln/access-control/missing-auth · vuln/logic/missing-check

Key info#

ProtocolTaiko
FindingOpenZeppelin Taiko protocol audit, AuditVault 34318
Vulnerable codeAssignmentHook.onBlockProposed
HarmAn attacker prover receives an honest prover's entire 1000 TKO balance
Chainlocal synthetic

TL;DR#

proposeBlock stores the caller-supplied coinbase unless it is the zero address. onBlockProposed pays the assigned prover with transferFrom(coinbase, prover, fee). An honest prover has approved the hook for type(uint256).max and holds 1000 TKO. A proposer sets coinbase to that prover, the fee to 1000 TKO, and the assigned prover to themselves. The hook moves all 1000 TKO. The honest prover's balance is 0. The control passes coinbase zero, the caller pays a 10 TKO fee, and the honest prover's balance does not change.

This is the same coinbase pull as the later Code4rena report, described here as theft of a prover's allowance rather than as one proposer forcing another proposer to pay a partial fee.

The vulnerable code#

SOLIDITY
if (coinbase == address(0)) {
    coinbase = msg.sender;
}
metaCoinbase = coinbase;
hook.onBlockProposed(assignedProver, coinbase, proverFee);
SOLIDITY
paidProver = assignedProver;
paidFee = proverFee;
// @> VULN: fee is transferFrom(meta.coinbase), so a proposer can spend another account's allowance
feeToken.transferFrom(coinbase, assignedProver, proverFee);

The audited call is safeTransferFrom(_meta.coinbase, _blk.assignedProver, proverFee) in AssignmentHook. The hook is the token spender, so the allowance that matters is the allowance to the hook.

Root cause#

Coinbase is treated as the fee payer, and the proposer chooses both that address and the fee. A prover who approved the hook so it could pay its own fees has authorized every later proposer to name them.

Preconditions#

The honest prover holds 1000 TKO and has approved the hook for the maximum uint256. The attacker can call proposeBlock and can choose the assigned prover.

Attack walkthrough#

  1. Read the honest prover's 1000 TKO balance and the max allowance.
  2. proposeBlock(honestProver, attackerProver, 1000 TKO).
  3. Coinbase is not replaced.
  4. The hook transfers 1000 TKO from the honest prover to the attacker prover.
  5. The honest prover's balance is 0.

The control uses coinbase zero and a 10 TKO fee paid by the caller. The honest prover still has 1000 TKO.

Diagrams#

flowchart TD A["Prover approves the hook for the max uint256"] --> B["Prover holds 1000 TKO"] B --> C["Attacker sets coinbase to that prover"] C --> D["Fee is set to 1000 TKO"] D --> E["Hook transferFrom pays the attacker prover"] E --> F["Honest prover balance is 0"]

Impact#

The honest prover loses 1000 TKO in one proposal. The attacker chooses the recipient. Any account that has approved the hook, up to its token balance, can be drained the same way.

Remediation#

Pay the fee from the proposeBlock caller. Do not use meta.coinbase as the transferFrom source. Taiko's later patch records the proposer separately and charges that address.

How to reproduce#

BASH
cd 34318-vulnerability-in-block-proposal-function-allows-token-theft_exp
forge test -vv

Sources#


Sources & further analysis#

Reproductions & code

Alerts & third-party analyses

  • Web3Sec X hacked database: search.
  • Rekt leaderboard: search.
  • Solodit incident search: search.

These dashboards index community alerts tweets, post-mortems, and independent write-ups. Reach them through the protocol name above to cross-check this reproduction against other analyses.