All incidents

C87C Router tokenReceived Authentication Bypass Drains Victim-Approved USDT

Share
Feb 13, 2026 17:05 UTCAttackLoss: 13,906.72 USDTManually checked2 exploit txWindow: 48s
Estimated Impact
13,906.72 USDT
Label
Attack
Exploit Tx
2
Addresses
1
Attack Window
48s
Feb 13, 2026 17:05 UTC → Feb 13, 2026 17:06 UTC

Exploit Transactions

TX 1Ethereum
0xb944c628443e62cc4445850c2e174a4dd799805ffacc4661f7efead369d6b86e
Feb 13, 2026 17:05 UTCExplorer
TX 2Ethereum
0x54bb31a5a1f5cb4d47f89d11d55c5e64e0de7b06956ba7c21d2062950357ccd5
Feb 13, 2026 17:06 UTCExplorer

Victim Addresses

0x222e674fb1a7910ccf228f8aecf760508426b482Ethereum

Loss Breakdown

13,906.72USDT

Similar Incidents

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 0x2c760082a052607cd36bd6cd46f705d752ae35430e92c16ce8f0be73d03dde4f with allowance 9007199254740991
  • At pre-state block 24449244: victim USDT balance=13906718432, allowance to c87c =9007199234740991

Adversary context:

  • Attacker EOA: 0x4fd9669fb676ea2ace620afb6178ae300ecfd8a9
  • Helper contract: 0xc8540a70aa191651d7cf8ed854ea3d346c897b2a
  • Deployment linkage is deterministic (nonce 0 from 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 from 0x33ce7aee0 to 0.
  • Victim allowance-related slot (0x0f0b...) decreases by exactly 13906718432.

Exploit preconditions (all satisfied at block 24449244):

  1. Victim has spendable allowance to c87c.
  2. Victim has nonzero token balance.
  3. Attacker can directly call unauthenticated tokenReceived with crafted payload.
  4. Swap/pool route exists and callback path is reachable.

5. Adversary Flow Analysis

The adversary execution is a two-transaction ACT sequence:

  1. Deployment stage (0xb944c628..., block 24449241)
  • Attacker EOA deploys helper 0xc854....
  1. Exploit stage (0x54bb31a5..., block 24449245)
  • 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=false as 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 wei native
  • 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

7. References

  1. Seed exploit trace (tx 0x54bb31a5...): artifacts/collector/seed/1/0x54bb31a5a1f5cb4d47f89d11d55c5e64e0de7b06956ba7c21d2062950357ccd5/trace.cast.log
  2. c87c decompiled tokenReceived path: artifacts/collector/iter_1/contract/1/0xc87c815c03b6cd45880cbd51a90d0a56ecfba9da/decompile_sol/decompiled.sol
  3. c87c disassembly with transferFrom dispatch (0x23b872dd): artifacts/collector/iter_1/contract/1/0xc87c815c03b6cd45880cbd51a90d0a56ecfba9da/disassembly/disassembled.asm
  4. Victim approval transaction decode (0x2c760082...): artifacts/collector/iter_1/tx/1/0x2c760082a052607cd36bd6cd46f705d752ae35430e92c16ce8f0be73d03dde4f/calldata_decoded.txt
  5. Participant-focused exploit deltas: artifacts/collector/iter_1/state_diff/1/0x54bb31a5a1f5cb4d47f89d11d55c5e64e0de7b06956ba7c21d2062950357ccd5/state_diff_participants_normalized.json
  6. Prestate raw diff (USDT storage pre/post): artifacts/collector/iter_1/state_diff/1/0x54bb31a5a1f5cb4d47f89d11d55c5e64e0de7b06956ba7c21d2062950357ccd5/prestate_diff_raw.json
  7. Prestate balance diff (attacker ETH pre/post): artifacts/collector/iter_1/state_diff/1/0x54bb31a5a1f5cb4d47f89d11d55c5e64e0de7b06956ba7c21d2062950357ccd5/balance_diff_prestate.json
C87C Router tokenReceived Authentication Bypass Drains Victim-Approved USDT | Clara