Tara Light Client Quorum-Check Ordering Bug Enables Forged Bridge Finalization and WETH Drain
Exploit Transactions
0x2074d4f995bc4d009c25a40ea746be4e661d9a7a15fdfc9fdc9a419394986e60Victim Addresses
0x34eae07195d83305a0be4786f2ab8705613c69c0Ethereum0x0ecef178f5831494b6275ce838eda09bb67fd869EthereumLoss Breakdown
Similar Incidents
TaraClient finalizeBlocks Quorum-Bypass via In-Tx Validator Set Mutation
44%Indexed Finance DEFI5 gulp/reindex bug enables SUSHI flash-swap drain
32%WETH Drain via Unprotected 0xfa461e33 Callback on 0x03f9-62c0
31%LiteV3 Bridge Aggregator Proxy Initialization Race Enabled Unauthorized UUPS Takeover
31%C87C Router tokenReceived Authentication Bypass Drains Victim-Approved USDT
31%SorraV2 staking withdraw bug enables repeated SOR reward drain
30%Root Cause Analysis
Tara Light Client Quorum-Check Ordering Bug Enables Forged Bridge Finalization and WETH Drain
1. Incident Overview TL;DR
On Ethereum mainnet, transaction 0x2074d4f995bc4d009c25a40ea746be4e661d9a7a15fdfc9fdc9a419394986e60 (block 24513770) executes a deterministic ACT exploit against Tara bridge light-client integration and downstream DPP liquidity. The attacker-controlled transaction updates validator weights and then passes quorum checks inside the same finalizeBlocks(((uint256,bytes32,bytes32,bytes32,uint256),(address,int32)[])[],(bytes32,bytes32)[]) execution. This forged finalization sets a fake bridge root and unlocks bridge state application that mints TEST tokens into DPP pool 0x0ecef178f5831494b6275ce838eda09bb67fd869.
The root cause is strict ordering failure in the light-client implementation 0x34eae07195d83305a0be4786f2ab8705613c69c0: calldata-provided vote deltas are applied to validatorVoteCounts and totalWeight before quorum verification. The exploit then sells minted quote-side assets through DPP, drains WETH liquidity, unwraps WETH to ETH, and exits with net ETH profit after gas.
ACT framing is satisfied: the exploit path uses public contracts, public calldata formats, and public on-chain state only, with no privileged keys or governance permissions.
2. Key Background
Protocol context required to understand this incident:
- Light-client entry:
0xcdf14446f78ea7ebcaa62fdb0584e4d2e536b999. - Light-client implementation:
0x34eae07195d83305a0be4786f2ab8705613c69c0. - Bridge apply proxy path:
0x359cf536b1fd6248ebad1449e1b3727cab33a01d->0x089f8b425e4ab63b46e1e88688c138e44fbddff6-> state appliers0xff235ea751964bba3887392de889a834a2bdfbde/0xa134a9513589fa446f0a4da548a18488e38729b1. - Liquidity target: DPPAdvanced pool
0x0ecef178f5831494b6275ce838eda09bb67fd869. - Assets:
TESTtoken0x2f42b7d686ca3effc69778b6ed8493a7787b4d6e, WETH0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2.
Relevant storage model and selectors:
finalizeBlocksselector:0x5d0d5734.validatorVoteCountsmapping slot index:8.totalWeightstorage slot index:9.finalizedBridgeRootsmapping slot index:7.- Derived slot for
validatorVoteCounts(0x7e5f4552091a69125d5dfcb7b8c2659029395bdf):0x1d373a3e5853edae26cb3f8cbbdf6744bd2f1f679148aa7cf8cb59e6414d326e. - Derived slot for
finalizedBridgeRoot(573):0xfb63f75781a61ebcf1cdfe0e89d9459768093e285c1d9c6cbaf75faa636e558a.
3. Vulnerability Analysis & Root Cause Summary
This is an ATTACK-class logic bug, not a benign MEV arbitrage pattern. The invariant should be: quorum for a block finalization must be computed from the pre-existing validator set, not a validator set mutated by the same untrusted input payload. In the vulnerable flow, finalizeBlocks consumes (validator, int32 delta) pairs, updates validator weights and totalWeight, and only afterward computes/validates signer weight thresholds. Because of this order, the call can self-authorize by injecting a high weight (here int32.max) for a signer whose signature is supplied in the same call.
The exploit then sets forged finalized state (getFinalizedBridgeRoot(573)), calls bridge apply logic, mints 4e33 TEST into DPP, and sells quote-side liquidity for WETH. Net effect is measurable value extraction from protocol-integrated liquidity under an unauthorized finalized root.
Security principles violated:
- Check-before-effect sequencing for consensus/quorum logic.
- Trust-boundary separation between proposal payload and authorization state.
- Non-circular authorization (state updates must not authorize themselves).
4. Detailed Root Cause Analysis
4.1 ACT Opportunity Definition
- Pre-state block
B:24513769. - Adversary-crafted sequence
b: single transaction0x2074d4f995bc4d009c25a40ea746be4e661d9a7a15fdfc9fdc9a419394986e60. - Success predicate:
- Monetary: attacker net ETH increases after fees.
- Non-monetary: finalized bridge root is accepted under attacker-mutated quorum state.
The transaction is permissionless to submit and uses only public functions and public data. No privileged actor assumptions are required.
4.2 Code-Level Breakpoint (Victim Implementation)
Opcode evidence from the light-client implementation disassembly shows update-before-verification ordering.
0005c0 JUMPDEST
0005e2 PUSH2 0db3
0005e5 JUMP ; enter validator-update loop first
...
000609 PUSH1 00
00060d PUSH1 09
00060f SLOAD ; totalWeight read after update path
...
000660 PUSH2 0887
000663 JUMP ; jump to signer/quorum routine
...
000887 JUMPDEST ; signature/weight verification routine
...
000db3 JUMPDEST ; validator update loop entry
...
000eb8 SIGNEXTEND ; int32 delta handling from calldata
...
000f00 ... SSTORE ; write validatorVoteCounts(slot8,address)
000f1e SSTORE ; write totalWeight(slot9)
000f22 PUSH2 0db8
000f25 JUMP ; loop continuation
This directly matches the claimed breakpoint: update loop (0xdb3) is reached before threshold and signer-weight checks (0x609, 0x887).
4.3 On-Chain Execution Evidence
Trace evidence for the seed transaction shows the exact malicious payload and downstream execution:
0xcDF14446...::finalizeBlocks(
[((25384000, ..., 0xcfc74ddd..., 0xa5010456..., 573),
[(0x7bD73663..., -2000000), (0x7E5F4552..., 2147483647)])],
[(0xe834c996..., 0x1667be5c...)]
)
0x34EAe071...::finalizeBlocks(...) [delegatecall]
PRECOMPILES::ecrecover(...) -> 0x7E5F4552091A69125d5DfCb7b8C2659029395Bdf
State-change and bridge-apply evidence in the same trace:
@ 0xfb63f75781a61ebcf1cdfe0e89d9459768093e285c1d9c6cbaf75faa636e558a: 0 -> 0xa5010456...
0x359CF536...::6cd50a67(...)
0x089F8B42...::6cd50a67(...) [delegatecall]
Receipt and token-flow evidence confirm mint and extraction:
{
"status": "0x1",
"gasUsed": "0x20241e",
"effectiveGasPrice": "0xa5df902",
"contractAddress": "0x8cac44586a4779e9ebdd698f8158cfc7a2fe80b7"
}
{
"erc20_balance_deltas": [
{
"token": "0x2f42b7d686ca3effc69778b6ed8493a7787b4d6e",
"holder": "0x0ecef178f5831494b6275ce838eda09bb67fd869",
"delta": "4000000000000000000000000000000000"
}
],
"native_balance_deltas": [
{
"address": "0x9c8624440d1ae80f173694e6a4f650e2418b8213",
"delta_wei": "1141129020340209213"
},
{
"address": "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2",
"delta_wei": "-1141488345067626941"
}
]
}
4.4 Deterministic Arithmetic Checks
gasUsed = 0x20241e = 2,106,398.effectiveGasPrice = 0x0a5df902 = 173,930,754 wei.- Fee =
366,367,392,364,092 wei. - Attacker gross ETH in from exploit path minus net balance delta equals fee exactly.
Weight/quorum arithmetic from storage diffs:
totalWeight before = 0x80182d65 = 2,149,068,133.totalWeight after = 0xfff9a8e4 = 4,294,551,780.- Added signer weight after update =
0x7fffffff = 2,147,483,647. - Post-update majority threshold =
2,147,275,891. - Signer margin =
207,756.
This is sufficient to show self-authorization became possible only because update and verification happen in the same call in the wrong order.
5. Adversary Flow Analysis
Adversary-related accounts:
- EOA
0x9c8624440d1ae80f173694e6a4f650e2418b8213: sender, gas payer, final ETH recipient. - Contract
0x8cac44586a4779e9ebdd698f8158cfc7a2fe80b7: created in exploit tx. - Contract
0x6dbb7c95ba397e8342777e068a1887418a42fbb4: in-tx runner that executes exploit sequence.
Victim-side candidates in the exploit path:
0xcdf14446f78ea7ebcaa62fdb0584e4d2e536b999(light-client entry).0x34eae07195d83305a0be4786f2ab8705613c69c0(light-client implementation).0x359cf536b1fd6248ebad1449e1b3727cab33a01d(bridge connector proxy).0x0ecef178f5831494b6275ce838eda09bb67fd869(DPPAdvanced pool).
Lifecycle stages from evidence:
- Adversary setup
- Prior same-EOA contract creations:
0x2d9abc22...(block24512117) and0x9db1de9d...(block24513681).
- Prior same-EOA contract creations:
- Forged finalization + bridge apply
- Tx
0x2074d4f9...injects vote deltas, finalizes forged root, executes bridge apply call0x6cd50a67.
- Tx
- Liquidity extraction + profit realization
- Same tx performs DPP
sellQuote, receives WETH, unwraps to ETH, and forwards value back to attacker EOA.
- Same tx performs DPP
End-to-end exploit sequence is single-transaction and permissionless.
6. Impact & Losses
Measured losses/impact from canonical deltas:
- WETH depletion from protocol-integrated liquidity:
1,141,488,345,067,626,941wei (1.141488345067626941 WETH). - Attacker net ETH increase (after gas):
1,141,129,020,340,209,213wei. - Test token minted into DPP pool via forged bridge state:
4,000,000,000,000,000,000,000,000,000,000,000raw units.
Scope:
- Consensus-like trust of bridge finalization is broken for the affected path.
- Downstream integrator liquidity (
DPPAdvanced) is directly drained as a consequence of forged finalized root acceptance.
7. References
Primary reproducible evidence:
- Seed tx metadata (
0x2074d4f9...):/workspace/session/artifacts/collector/seed/1/0x2074d4f995bc4d009c25a40ea746be4e661d9a7a15fdfc9fdc9a419394986e60/metadata.json - Full execution trace (cast
-vvvvv):/workspace/session/artifacts/collector/iter_1/tx/1/0x2074d4f995bc4d009c25a40ea746be4e661d9a7a15fdfc9fdc9a419394986e60/trace.cast.requery.log - Canonical receipt:
/workspace/session/artifacts/collector/iter_1/tx/1/0x2074d4f995bc4d009c25a40ea746be4e661d9a7a15fdfc9fdc9a419394986e60/receipt.requery.json - Native/ERC20 balance deltas:
/workspace/session/artifacts/collector/iter_1/tx/1/0x2074d4f995bc4d009c25a40ea746be4e661d9a7a15fdfc9fdc9a419394986e60/balance_diff.requery.json - Light-client implementation disassembly:
/workspace/session/artifacts/collector/iter_1/contract/1/0x34eae07195d83305a0be4786f2ab8705613c69c0/disassemble/runtime-disassembled.asm - Slot derivation notes:
/workspace/session/artifacts/auditor/iter_1/slot_derivations.txt - Numeric consistency checks:
/workspace/session/artifacts/auditor/iter_1/numeric_checks.txt
Incident-relevant transactions (complete set from root cause artifact):
- Related setup:
0x2d9abc22d86f2097cc0a43fcfbc84525c31e08fb20fb692bcd146c798888ba63 - Related setup:
0x9db1de9d9af98286f952ef3638a4d8034d154d5eca11049ff08c4ccd828c9e26 - Adversary-crafted exploit:
0x2074d4f995bc4d009c25a40ea746be4e661d9a7a15fdfc9fdc9a419394986e60