C87C Router tokenReceived Authentication Bypass Drains Victim-Approved USDT
Exploit Transactions
Victim Addresses
0x222e674fb1a7910ccf228f8aecf760508426b482EthereumLoss Breakdown
Similar Incidents
LiFi router allowance-drain exploit steals approved holder tokens
34%WBTC Drain via Insecure Router transferFrom Path
33%WETH9 Balance Drain via Router 0x5697-1751 Using Victim
32%Spectra Router KYBERSWAP arbitrary call drains SdCrvCompounder
31%GPv2Settlement allowance leak lets router drain WETH and USDC
31%TaraClient finalizeBlocks Quorum-Bypass via In-Tx Validator Set Mutation
31%Root Cause Analysis
C87C Router tokenReceived Authentication Bypass Drains Victim-Approved USDT
1. Incident Overview TL;DR
At Ethereum block 24449245, an unprivileged adversary realized an ACT exploit by calling helper contract 0xc8540a70aa191651d7cf8ed854ea3d346c897b2a, which then invoked c87c router 0xc87c815c03b6cd45880cbd51a90d0a56ecfba9da with forged victim payer context. The exploit transaction is 0x54bb31a5a1f5cb4d47f89d11d55c5e64e0de7b06956ba7c21d2062950357ccd5, and the attacker deployment transaction is 0xb944c628443e62cc4445850c2e174a4dd799805ffacc4661f7efead369d6b86e.
The critical exploit predicate is satisfied when USDT.transferFrom(victim, ddbb_pool, amount) is executed through c87c callback flow while transaction sender is not the victim. In the exploit, TetherToken::transferFrom(0x222e..., 0xDdBB..., 13906718432) is executed inside uniswapV3SwapCallback, and the victim is drained from 13906718432 USDT to 0.
This is classified as ACT (is_act=true) because execution requires only public on-chain state plus permissionless transactions. Attacker value changes are deterministic in evidence: before exploit, attacker had USDT 0 and ETH 19582320258858240 wei; after exploit, attacker had USDT 13801642298 and ETH 49765498299821006 wei.
2. Key Background
The vulnerable component is unverified c87c router bytecode at 0xc87c815c03b6cd45880cbd51a90d0a56ecfba9da, with relevant selectors:
0x8943ec02(tokenReceived(address,uint256,bytes))0x1a9d82d5(exactInputSingle(...))0xfa461e33(uniswapV3SwapCallback(...))
Victim context:
- Victim EOA:
0x222e674fb1a7910ccf228f8aecf760508426b482 - Victim approved c87c in tx
0x2c760082a052607cd36bd6cd46f705d752ae35430e92c16ce8f0be73d03dde4fwith allowance9007199254740991 - At pre-state block
24449244: victimUSDT balance=13906718432, allowance to c87c=9007199234740991
Adversary context:
- Attacker EOA:
0x4fd9669fb676ea2ace620afb6178ae300ecfd8a9 - Helper contract:
0xc8540a70aa191651d7cf8ed854ea3d346c897b2a - Deployment linkage is deterministic (
nonce 0from attacker computes helper address)
3. Vulnerability Analysis & Root Cause Summary
Root cause category is ATTACK. The core issue is unauthenticated payer injection in c87c tokenReceived: payer context is set from attacker-controlled calldata and then attacker-provided bytes are delegatecalled in c87c context. This allows downstream callback logic to execute ERC20 transferFrom using a spoofed payer (victim).
The violated invariant is explicit: callback-driven transferFrom(payer, ...) must derive payer from authenticated source, not untrusted external arguments. The code-level breakpoint is c87c selector 0x8943ec02, where call_sender is set from arg0 and arbitrary payload is executed via delegatecall without trusted-caller validation. During exploit execution, the delegated path reaches swap callback and triggers USDT.transferFrom(victim, pool, 13906718432).
Because victim had both nonzero balance and nonzero allowance to c87c, and because attacker could call tokenReceived directly, the exploit is permissionless and deterministic.
4. Detailed Root Cause Analysis
The decompiled c87c tokenReceived path shows attacker-controlled payer write plus delegatecall:
function Unresolved_8943ec02(address arg0, uint256 arg1, uint256 arg2) public returns (uint256) {
...
call_sender = (uint96(call_sender)) | (address(arg0));
token_sender = msg.sender | (uint96(token_sender));
...
(bool success, bytes memory ret0) = address(this).Unresolved_(var_g); // delegatecall
...
}
Trace evidence from exploit tx 0x54bb31a5... confirms the exact unauthorized flow:
0xC87C...::tokenReceived(0x222E..., 0, <payload>)
-> 0xC87C...::exactInputSingle(...) [delegatecall]
-> 0xC87C...::uniswapV3SwapCallback(..., 13906718432, ...)
-> TetherToken::transferFrom(0x222E..., 0xDdBB..., 13906718432)
Deterministic state effects in the same trace include:
- Victim USDT storage slot (
0xc555...) transitions from0x33ce7aee0to0. - Victim allowance-related slot (
0x0f0b...) decreases by exactly13906718432.
Exploit preconditions (all satisfied at block 24449244):
- Victim has spendable allowance to c87c.
- Victim has nonzero token balance.
- Attacker can directly call unauthenticated
tokenReceivedwith crafted payload. - Swap/pool route exists and callback path is reachable.
5. Adversary Flow Analysis
The adversary execution is a two-transaction ACT sequence:
- Deployment stage (
0xb944c628..., block24449241)
- Attacker EOA deploys helper
0xc854....
- Exploit stage (
0x54bb31a5..., block24449245)
- EOA calls helper exploit entrypoint.
- Helper invokes c87c
tokenReceived(victim, 0, payload). - Delegated swap reaches DDBB pool and callback.
- Callback executes
USDT.transferFrom(victim, pool, 13906718432). - Swapped outputs are realized to attacker-controlled addresses; attacker EOA receives USDT and positive native delta.
Observed adversary-related accounts:
- EOA:
0x4fd9669fb676ea2ace620afb6178ae300ecfd8a9 - Contract:
0xc8540a70aa191651d7cf8ed854ea3d346c897b2a
Victim/protocol entities:
- Victim EOA:
0x222e674fb1a7910ccf228f8aecf760508426b482(is_verified=falseas EOA) - USDT:
0xdac17f958d2ee523a2206206994597c13d831ec7 - c87c router:
0xc87c815c03b6cd45880cbd51a90d0a56ecfba9da - ddbb pool:
0xddbb864c2541e27152dbb87037ece852afb1faf5
6. Impact & Losses
Measured loss:
- Victim loss overview:
USDT 13,906,718,432(token units)
Participant-focused exploit deltas from state diff:
- Attacker EOA
0x4fd9...:+13,801,642,298 USDT,+30,183,178,040,962,766 weinative - Victim EOA
0x222e...:-13,906,718,432 USDT - DDBB pool
0xddbb...:+105,076,134 USDT,-30,473,086,523,329,718 wei WETH
Fee and valuation details:
- Transaction fee:
289,908,482,366,952 wei - Attacker value before/after (deterministic):
- Before:
USDT 0,ETH 19582320258858240 wei - After:
USDT 13801642298,ETH 49765498299821006 wei
- Before:
7. References
- Seed exploit trace (tx
0x54bb31a5...):artifacts/collector/seed/1/0x54bb31a5a1f5cb4d47f89d11d55c5e64e0de7b06956ba7c21d2062950357ccd5/trace.cast.log - c87c decompiled
tokenReceivedpath:artifacts/collector/iter_1/contract/1/0xc87c815c03b6cd45880cbd51a90d0a56ecfba9da/decompile_sol/decompiled.sol - c87c disassembly with transferFrom dispatch (
0x23b872dd):artifacts/collector/iter_1/contract/1/0xc87c815c03b6cd45880cbd51a90d0a56ecfba9da/disassembly/disassembled.asm - Victim approval transaction decode (
0x2c760082...):artifacts/collector/iter_1/tx/1/0x2c760082a052607cd36bd6cd46f705d752ae35430e92c16ce8f0be73d03dde4f/calldata_decoded.txt - Participant-focused exploit deltas:
artifacts/collector/iter_1/state_diff/1/0x54bb31a5a1f5cb4d47f89d11d55c5e64e0de7b06956ba7c21d2062950357ccd5/state_diff_participants_normalized.json - Prestate raw diff (USDT storage pre/post):
artifacts/collector/iter_1/state_diff/1/0x54bb31a5a1f5cb4d47f89d11d55c5e64e0de7b06956ba7c21d2062950357ccd5/prestate_diff_raw.json - Prestate balance diff (attacker ETH pre/post):
artifacts/collector/iter_1/state_diff/1/0x54bb31a5a1f5cb4d47f89d11d55c5e64e0de7b06956ba7c21d2062950357ccd5/balance_diff_prestate.json