0x7CAE Approved-Spender Drain
Exploit Transactions
Victim Addresses
0x783e2f71d8967bdee8aa2ba0f3b9f402ac871365Ethereum0x8f159f13f64db18b8a0742c86fa8b225cead6c5dEthereum0x899b11881f977aeb5d9fac5105ce62c877f11763Ethereum0x9eaaeab7255296e68ad1f12b969b9e30d1806c9dEthereum0xc21a3b81efbba41dd319191b07a20eb1f5eebd61Ethereum0x0a78fbeb89ee251c0d78e0eeb5e6bb7524a8939fEthereum0x512e9701d314b365921bcb3b8265658a152c9ffdEthereum0xf2cdd8b147802a07f862c9dc125190e0653795a2Ethereum0x7e585b185fc67bc5f815b7abf459300418aa9f97Ethereum0x26d61e57c44525d25aad4ef20bce3f7aa9d64c4cEthereum0x7b05363f549c929c3da930f6728e3d74806e4103EthereumLoss Breakdown
Similar Incidents
C87C Router tokenReceived Authentication Bypass Drains Victim-Approved USDT
38%LiFi router allowance-drain exploit steals approved holder tokens
37%V3Utils Arbitrary Call Drain
36%USDTStaking Approval Drain
36%Dexible selfSwap allowance drain
36%WBTC Drain via Insecure Router transferFrom Path
34%Root Cause Analysis
0x7CAE Approved-Spender Drain
1. Incident Overview TL;DR
At Ethereum block 17646142, EOA 0xc0ccff0b981b419e6e47560c3659c5f0b00e4985 used freshly deployed helper contract 0xf466f9f431aea853040ef837626b1c59cc963ce2 to drain previously approved balances from multiple token holders through router 0x7caec5e4a3906d0919895d113f7ed9b3a0cbf826. The attacker contract finished the transaction holding BONE, WOOF, SANI, 0NE, CELL, and USDC that had been pulled directly from victim EOAs via transferFrom.
The root cause is a public approved-spender surface in 0x7cae. Its public selector 0x5ffe72b7 accepts a caller-chosen pool and later trusts that pool inside uniswapV3MintCallback, where the router spends from a payer decoded out of callback data. Because neither the entrypoint nor the callback authorizes the payer or constrains the recipient pool to a trusted set, any public caller can redirect balances from any address that already approved 0x7cae.
2. Key Background
0x7cae is a long-lived public router, not a single-use attacker deployment. Its creation metadata shows it was deployed in tx 0xcf8e9b4005763e5cbe8d5695a18b16005ab4e115894e203352bcad59e38705ae, and the collected Etherscan tx list shows successful public calls to selector 0x5ffe72b7 well before the incident. This matters because the exploit surface was already exposed to any searcher that inspected public state and bytecode.
The disassembly shows the router stores WETH in slot 0x98 and only checks that a caller-supplied pool reports WETH as token0() or token1(). An attacker can satisfy that requirement with a minimal custom pool contract. The helper contract 0xf466 was therefore only an execution wrapper: it is not the vulnerability, and the ACT property comes from the public router itself.
The victim side is a set of EOAs that had granted allowances to 0x7cae. The updated auditor artifact victim_code_check.txt confirms the listed affected holder addresses have no runtime code, so the incident is not explained by privileged victim contracts or hidden stakeholder logic.
3. Vulnerability Analysis & Root Cause Summary
This is an ATTACK-class ACT exposure centered on missing authorization in a public spender path. The critical invariant is straightforward: if router 0x7cae is approved as an ERC20 spender, any external execution path that spends from payer must verify that the caller is authorized to act for that payer and that the payout recipient is an approved route. The disassembly-derived breakpoint is the chain 0x5ffe72b7 at offset 0x067a -> uniswapV3MintCallback at offset 0x1117 -> pay() at offset 0x1bdd -> safeTransferFrom() at offset 0x20f6.
At 0x067a, the router stores a caller-supplied pool in slot 0x97 and immediately calls pool.mint(address(this), ...). At 0x1117, the callback only checks that msg.sender == sload(0x97), so any pool chosen by the attacker is trusted for that execution. From there, pay() routes to safeTransferFrom() whenever payer != address(this), and the payer comes directly from callback data. That lets a public caller cause transferFrom(victim, attacker-controlled recipient, amount) as long as the victim has already approved 0x7cae.
Observed router flow from the disassembly:
0x067a: public 0x5ffe72b7 stores caller-supplied pool in slot 0x97 and calls pool.mint(address(this), ...)
0x1117: uniswapV3MintCallback only checks msg.sender == slot 0x97
0x1bdd: pay() dispatch
0x20f6: safeTransferFrom(token, payer, pool, amount) when payer != address(this)
4. Detailed Root Cause Analysis
The seed transaction 0xc42fc0e22a0f60cc299be80eb0c0ddce83c21c14a3dddd8430628011c3e20d6b proves the exploit path on-chain. The call trace repeatedly shows the helper calling router selector 0x5ffe72b7, the router invoking the helper-controlled mint, and the callback forcing token spends from victim EOAs.
Observed seed trace excerpt:
0xF466...::0a5fede2(...)
0x7CAE...::5ffe72b7(...)
0xF466...::mint(0x7CAE..., 0, 0, 0, ...)
0x7CAE...::uniswapV3MintCallback(0, 1900000000, 0x0000000000000000000000007b05363f549c929c3da930f6728e3d74806e4103)
0xA0b86991...::transferFrom(0x7b05363f549c929C3dA930f6728e3D74806E4103, 0xF466F9f431aEa853040EF837626b1c59CC963ce2, 1900000000)
That exact USDC leg is mirrored in the balance diff. Victim 0x7b05363f549c929c3da930f6728e3d74806e4103 drops from 1900052720 to 52720, while helper 0xf466... rises from 0 to 1900000000. The same trace pattern repeats across the other affected tokens and holders. The router is therefore not merely interacting with attacker code; it is actively exercising its own spender allowance against third-party payers supplied in callback data.
The exploit conditions in the structured root cause hold against the collected evidence:
- A victim must hold tokens and have a live allowance to
0x7cae. - The router must be unpaused.
- The attacker must supply a pool that reports WETH on one side and can trigger the callback.
- The target token must honor
transferFromfor0x7cae.
The fork PoC independently validates the mechanism without using the incident attacker identities. It recreates the historical USDC drain against the same approved victim with a fresh attacker EOA and a fresh locally deployed pool shim, which is consistent with the ACT framing.
5. Adversary Flow Analysis
The attack unfolded in two adversary-crafted transactions.
- Tx
0x208f27f35fe302366252741c8cebe0d8861859ae9d7b17f8b2c62156bf5c8e4fat block17646141deployed helper0xf466f9f431aea853040ef837626b1c59cc963ce2. - Tx
0xc42fc0e22a0f60cc299be80eb0c0ddce83c21c14a3dddd8430628011c3e20d6bat block17646142used that helper to batch 31 callback-driven drains across previously approved holders.
The helper's job was operational, not privileged. For each drain leg it selected a token, victim address, and amount, then called the public router path. The router accepted the helper as the pool, entered uniswapV3MintCallback, decoded the victim from callback data, and executed transferFrom into the helper. The helper therefore accumulated the drained balances solely because the public router transferred them there.
The adversary-related accounts are defensibly identified:
0xc0ccff0b981b419e6e47560c3659c5f0b00e4985: deploying EOA and sender of the seed drain transaction.0xf466f9f431aea853040ef837626b1c59cc963ce2: freshly deployed helper contract and sole recipient of drained balances.
The affected holder set in the submitted analysis contains 11 EOAs: 0x783e2f71d8967bdee8aa2ba0f3b9f402ac871365, 0x8f159f13f64db18b8a0742c86fa8b225cead6c5d, 0x899b11881f977aeb5d9fac5105ce62c877f11763, 0x9eaaeab7255296e68ad1f12b969b9e30d1806c9d, 0xc21a3b81efbba41dd319191b07a20eb1f5eebd61, 0x0a78fbeb89ee251c0d78e0eeb5e6bb7524a8939f, 0x512e9701d314b365921bcb3b8265658a152c9ffd, 0xf2cdd8b147802a07f862c9dc125190e0653795a2, 0x7e585b185fc67bc5f815b7abf459300418aa9f97, 0x26d61e57c44525d25aad4ef20bce3f7aa9d64c4c, and 0x7b05363f549c929c3da930f6728e3d74806e4103.
6. Impact & Losses
The seed transaction drained balances from 11 holder EOAs across six ERC20 assets. The collected balance diff attributes the following net losses to the unauthorized router-spend path:
BONE:20121200201296387000000WOOF:37656666000000000000000000SANI:24609683809494049098545523490NE:2584625207128315973299900000000CELL:10966052921140276000000USDC:1900000000
For 0NE, a portion of the transfer flow was routed to tax and burn destinations during transferFrom, but the holder-side negative delta still originates from the same unauthorized router callback path. The attacker-controlled helper contract ended the transaction holding the captured balances associated with the direct recipient legs, including the full 1900 USDC reproduced by the PoC.
7. References
- Seed deployment tx:
0x208f27f35fe302366252741c8cebe0d8861859ae9d7b17f8b2c62156bf5c8e4f - Seed drain tx:
0xc42fc0e22a0f60cc299be80eb0c0ddce83c21c14a3dddd8430628011c3e20d6b - Router creation tx:
0xcf8e9b4005763e5cbe8d5695a18b16005ab4e115894e203352bcad59e38705ae - Early successful router activity:
0x549b4212b808cd6b6e6952cb5e0f80798a60d4e34cc0e41fcaf0e50a91263dbband other successful0x5ffe72b7calls in the collected Etherscan tx list - Seed transaction metadata, trace, and balance diff under the collector artifacts for tx
0xc42fc0e... - Router and helper disassembly in the auditor artifacts:
7cae_disassembled.asmandf466_disassembled.asm - Victim EOA classification artifact:
artifacts/auditor/iter_1/victim_code_check.txt - Reproduction test:
poc/test/Exploit.sol