All incidents

WBTC Drain via Insecure Router transferFrom Path

Share
Jan 25, 2026 17:10 UTCAttackLoss: 36.92 WBTCManually checked1 exploit txWindow: Atomic

Root Cause Analysis

WBTC Drain via Insecure Router transferFrom Path

1. Incident Overview TL;DR

On Ethereum mainnet, an unprivileged adversary-controlled helper contract 0x5c92884dfe0795db5ee095e68414d6aabf398130 invoked router 0xd83d960debec397fb149b51f8f37dd3b5cfa8913 using selector 0x67b34120. The helper supplied calldata that leveraged an existing unlimited WBTC allowance from victim EOA 0x5240b03be5bc101a0082074666dd89ad883e1f9d to the router, causing WBTC.transferFrom to move exactly 36.91897652 WBTC from the victim to adversary EOA 0xe3e73f1e6ace2b27891d41369919e8f57129e8ea. The router then deposited the stolen WBTC, together with a small WETH contribution, into a Uniswap V3 liquidity position represented by NFT tokenId 0x1205ba owned by the helper contract.

Router contract 0xd83d960d...'s 0x67b34120 path constructs and executes ERC20.transferFrom calls using an owner address supplied directly in calldata, without binding that owner to msg.sender or to a verified signature. As a result, any caller with knowledge of a victim’s existing allowance can instruct the router to spend the victim’s WBTC on their behalf, making this an ACT (anyone-can-take) opportunity.

2. Key Background

The incident occurs on Ethereum mainnet and involves the following key actors and contracts:

  • Victim EOA 0x5240b03be5bc101a0082074666dd89ad883e1f9d, which holds WBTC and grants allowances.
  • WBTC ERC20 token 0x2260fac5e5542a773aa44fbcfedf7c193bc2c599, using 8 decimals.
  • Router contract 0xd83d960debec397fb149b51f8f37dd3b5cfa8913, which orchestrates swaps and liquidity provisioning using WETH9 and Uniswap V3.
  • WETH9 0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2 and Uniswap V3 NonfungiblePositionManager 0xc36442b4a4522e871399cd717abdd847ab11fe88, which receive liquidity and mint LP positions.
  • Adversary EOA 0xe3e73f1e6ace2b27891d41369919e8f57129e8ea and helper contract 0x5c92884dfe0795db5ee095e68414d6aabf398130.

The victim first submits an approval transaction giving router 0xd83d960d... an effectively unlimited WBTC allowance. This is a standard ERC20 approve call that any user can send via public RPC endpoints. Later, the adversary uses only public state (the allowance and victim balance) plus their own calldata to construct a transaction that directs the router to spend the victim’s WBTC into an adversary-controlled Uniswap V3 LP position.

The router implementation for selector 0x67b34120 (decompiled as Unresolved_67b34120) is critical. Heimdall decompilation of 0xd83d960d... shows a function that accepts numerous numeric and address arguments and then:

  • Selects a token address from calldata.
  • Selects owner, recipient, and amount fields from calldata.
  • Constructs a 0x23b872dd (transferFrom) payload.
  • Issues an external CALL to the token with that payload.

Crucially, there is no signature verification (no ecrecover or Permit2-style call) that binds the chosen owner address to msg.sender or to any off-chain authorization. The only gating mechanism for spending a victim’s tokens is the existing ERC20 allowance from the victim to the router.

3. Vulnerability Analysis & Root Cause Summary

The vulnerability is an access-control flaw in router 0xd83d960d...’s 0x67b34120 path. Instead of deriving the ERC20 owner from msg.sender or a verified signature, the router reads an owner address directly from attacker-controlled calldata and uses it to build a transferFrom call.

Given any ERC20 token T and account v, the intended invariant is:

For every call sequence originating from router 0xd83d960d... that results in T.transferFrom(v, r, a), the router must ensure that v either (i) is msg.sender at the router entry, or (ii) has provided a verifiable authorization (for example, a signature verified via ecrecover or a Permit2-style approval) that binds v to the specific transfer parameters (token, amount, recipient).

In the actual implementation, 0x67b34120 violates this invariant by allowing any caller to choose the owner parameter in calldata and relying solely on the router’s allowance at the token. When the adversary helper calls this function with WBTC as the token, the victim’s address as the owner, and the adversary’s address as the recipient, the router happily executes WBTC.transferFrom(victim, adversary, amount) using the pre-existing allowance. This makes the loss mechanism a deterministic ACT opportunity: any unprivileged account with the same on-chain information can repeat the strategy.

4. Detailed Root Cause Analysis

This section follows the concrete execution observed in the traces and decompiled code.

4.1 Victim approval and pre-state Before the exploit block, victim EOA 0x5240b03b... submits transaction 0x43aa58c764c762b65a86e6e343ee575fd163c8ff983f778fb693d9c9874be85c, an ERC20 approve call to WBTC:

// Seed metadata for tx 0x43aa58c7... (victim approval)
{
  "to": "0x2260fac5e5542a773aa44fbcfedf7c193bc2c599",
  "input": "0x095ea7b3000000000000000000000000d83d960debec397fb149b51f8f37dd3b5cfa8913ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
}

This matches a standard approve(spender = 0xd83d960d..., value = 2^256-1) call. After this transaction, the pre-state σ_B includes:

  • Victim EOA 0x5240b03b... holding WBTC.
  • An effectively unlimited WBTC allowance from 0x5240b03b... to router 0xd83d960d....

4.2 Router 0x67b34120 behavior from decompiled code Heimdall decompilation for 0xd83d960d... identifies the function corresponding to selector 0x67b34120:

/// @custom:selector    0x67b34120
/// @custom:signature   Unresolved_67b34120(uint256 arg0, uint256 arg1, uint256 arg2, uint256 arg3, uint256 arg4, address arg5, address arg6, uint256 arg7, uint256 arg8, uint256 arg9) public view
function Unresolved_67b34120(
    uint256 arg0,
    uint256 arg1,
    uint256 arg2,
    uint256 arg3,
    uint256 arg4,
    address arg5, // token
    address arg6, // owner or recipient
    uint256 arg7,
    uint256 arg8,
    uint256 arg9
) public view {
    // ... constructs calldata including 0x23b872dd and forwards via CALL ...
}

Later in the same decompiled contract, the router constructs calldata containing:

  • The selector 0x23b872dd (transferFrom(address,address,uint256)).
  • An owner address, a recipient address, and an amount, all sourced from calldata.
  • An external CALL to a token address also sourced from calldata.

There is no logic that derives the owner from msg.sender, and no branch that recovers a signature or calls a Permit2 contract to ensure the owner explicitly authorized this transfer. The only guard that can stop the transfer is the ERC20 allowance check inside WBTC itself—which permits any spender with sufficient allowance, including this router, to transfer tokens from the owner.

4.3 Adversary-crafted transaction and 0x67b34120 calldata The adversary crafts transaction 0x8f28a7f604f1b3890c2275eec54cd7deb40935183a856074c0a06e4b5f72f25a from EOA 0xe3e73f1e.... The receipt shows that this transaction:

  • Deploys helper contract 0x5c92884dfe0795db5ee095e68414d6aabf398130 (the contractAddress field).
  • Sends a call from the helper to router 0xd83d960d... with selector 0x67b34120.

The debug_trace_callTracer.json for this transaction captures the router call input, including the embedded 0x23b872dd payload:

// Excerpt from debug_trace_callTracer for tx 0x8f28a7f...
{
  "input": "0x67b34120...0000000000000000000000002260fac5e5542a773aa44fbcfedf7c193bc2c599...23b872dd0000000000000000000000005240b03be5bc101a0082074666dd89ad883e1f9d000000000000000000000000e3e73f1e6ace2b27891d41369919e8f57129e8ea00000000000000000000000000000000000000000000000000000000dc0de334...",
  "to": "0xd83d960debec397fb149b51f8f37dd3b5cfa8913"
}

Decoding this payload, we obtain:

  • Token: 0x2260fac5e5542a773aa44fbcfedf7c193bc2c599 (WBTC).
  • Owner: 0x5240b03be5bc101a0082074666dd89ad883e1f9d (victim EOA).
  • Recipient: 0xe3e73f1e6ace2b27891d41369919e8f57129e8ea (adversary EOA).
  • Amount: 0xdc0de334 (3,691,897,652 satoshis, or 36.91897652 WBTC with 8 decimals).

The trace then shows router 0xd83d960d... forwarding this payload in an external call to WBTC:

// Excerpt showing the WBTC.transferFrom call
{
  "to": "0x2260fac5e5542a773aa44fbcfedf7c193bc2c599",
  "input": "0x23b872dd0000000000000000000000005240b03be5bc101a0082074666dd89ad883e1f9d000000000000000000000000e3e73f1e6ace2b27891d41369919e8f57129e8ea00000000000000000000000000000000000000000000000000000000dc0de334"
}

This is the concrete breakpoint where the invariant is violated: the router issues WBTC.transferFrom(victim, adversary, 36.91897652 WBTC) using an owner address entirely chosen by the adversary via calldata, with no on-chain authentication step beyond the pre-existing ERC20 allowance.

4.4 Profit realization and LP mint The receipt and balance diffs for tx 0x8f28a7f... show:

  • A WBTC Transfer event:
// WBTC Transfer log for tx 0x8f28a7f...
{
  "address": "0x2260fac5e5542a773aa44fbcfedf7c193bc2c599",
  "topics": [
    "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
    "0x0000000000000000000000005240b03be5bc101a0082074666dd89ad883e1f9d",
    "0x000000000000000000000000e3e73f1e6ace2b27891d41369919e8f57129e8ea"
  ],
  "data": "0x00000000000000000000000000000000000000000000000000000000dc0de334"
}
  • A mint of Uniswap V3 LP NFT tokenId 0x1205ba to helper contract 0x5c9288... from NonfungiblePositionManager 0xc36442b4....
  • A small WETH deposit from the helper into the Uniswap pool.

The balance_diff.json confirms that:

  • The adversary EOA spends approximately 0.001463917118489758 ETH in gas and commits 100 wei as WETH liquidity.
  • There is no WBTC outflow from the adversary cluster {0xe3e73f1e..., 0x5c9288...}.

From the victim’s perspective, 36.91897652 WBTC leaves their account and ends up entirely under the control of the adversary cluster, partly in direct WBTC exposure and partly embedded in the LP position.

5. Adversary Flow Analysis

The adversary’s strategy is an ACT opportunity implemented in two main stages, both realizable by any unprivileged on-chain searcher with standard tooling.

Adversary strategy summary The adversary performs a two-step on-chain strategy:

  1. Rely on a victim EOA that has granted an unlimited WBTC allowance to router 0xd83d960d... and is using it for normal routing.
  2. Deploy a helper contract and call router function 0x67b34120 with calldata that:
    • Names WBTC as the token.
    • Selects the victim EOA as the owner.
    • Selects the adversary EOA as the recipient.
    • Specifies the desired WBTC amount to drain (bounded by the allowance and balance).
    • Encodes a Uniswap V3 position mint that deposits the stolen WBTC and a small amount of WETH into an LP position owned by the helper.

Adversary-related cluster accounts

  • 0xe3e73f1e6ace2b27891d41369919e8f57129e8ea (EOA):
    • Sender of attacker-crafted tx 0x8f28a7f....
    • Recipient of the stolen 36.91897652 WBTC in the WBTC Transfer event.
    • Funder and deployer of helper contract 0x5c9288....
  • 0x5c92884dfe0795db5ee095e68414d6aabf398130 (contract):
    • Deployed by 0xe3e73f1e... in tx 0x8f28a7f....
    • Calls router 0xd83d960d...’s 0x67b34120 function.
    • Receives Uniswap V3 LP NFT tokenId 0x1205ba, which embeds the stolen WBTC value.

Victim-related entities

  • Victim EOA 0x5240b03be5bc101a0082074666dd89ad883e1f9d, holder of the drained WBTC.
  • WBTC token 0x2260fac5e5542a773aa44fbcfedf7c193bc2c599.
  • Router contract 0xd83d960debec397fb149b51f8f37dd3b5cfa8913.

Lifecycle stages

  1. Victim allowance setup and router usage

    • Tx: 0x43aa58c764c762b65a86e6e343ee575fd163c8ff983f778fb693d9c9874be85c (block 24285462).
    • Mechanism: approve.
    • Effect: Victim EOA grants router 0xd83d960d... an effectively unlimited WBTC allowance on 0x2260fac5.... This is a standard user action and not an error by itself, but it enables the router to call WBTC.transferFrom on the victim’s behalf.
  2. Adversary helper contract deployment

    • Tx: 0x8f28a7f604f1b3890c2275eec54cd7deb40935183a856074c0a06e4b5f72f25a (block 24313234).
    • Mechanism: contract deployment from adversary EOA.
    • Effect: 0xe3e73f1e... deploys helper contract 0x5c9288..., funding it with 100 wei. The helper is designed to call router 0xd83d960d...’s 0x67b34120 function with crafted parameters.
  3. Adversary exploit execution and profit realization

    • Tx: same 0x8f28a7f... includes the exploit call.
    • Mechanism: helper calls router 0xd83d960d... (router_call) with 0x67b34120 and embedded 0x23b872dd payload.
    • Effect: Router executes WBTC.transferFrom(0x5240b03b..., 0xe3e73f1e..., 36.91897652 WBTC) and then mints Uniswap V3 LP NFT tokenId 0x1205ba to helper 0x5c9288..., embedding the stolen WBTC and some WETH into the LP position. The adversary cluster ends the transaction controlling both the stolen WBTC value and the LP NFT after paying a small amount of ETH in gas.

This flow uses only publicly observable data (the allowance, balances, and router ABI/behavior inferred from decompiled code) and standard EOA/contract transactions, satisfying the ACT criteria.

6. Impact & Losses

The measurable on-chain impact is concentrated in WBTC.

  • Token: WBTC (0x2260fac5e5542a773aa44fbcfedf7c193bc2c599).
  • Amount stolen: 36.91897652 WBTC.
  • Victim: EOA 0x5240b03be5bc101a0082074666dd89ad883e1f9d.
  • Adversary: EOA 0xe3e73f1e6ace2b27891d41369919e8f57129e8ea and helper contract 0x5c92884dfe0795db5ee095e68414d6aabf398130.

The WBTC leaves the victim’s balance via a single transferFrom and is immediately split between direct adversary exposure and an LP position held by the helper. There is no evidence in the analyzed traces of any compensating inflow to the victim within the relevant block range, so the 36.91897652 WBTC loss is realized in full in a single adversary-crafted transaction.

7. References

Key artifacts that support and reproduce this analysis:

  • Victim approve transaction 0x43aa58c764c762b65a86e6e343ee575fd163c8ff983f778fb693d9c9874be85c (trace and metadata):
    • artifacts/root_cause/seed/1/0x43aa58c764c762b65a86e6e343ee575fd163c8ff983f778fb693d9c9874be85c.
  • Adversary profit transaction 0x8f28a7f604f1b3890c2275eec54cd7deb40935183a856074c0a06e4b5f72f25a (receipt, traces, and balance diffs):
    • artifacts/root_cause/data_collector/iter_1/tx/1/0x8f28a7f604f1b3890c2275eec54cd7deb40935183a856074c0a06e4b5f72f25a.
  • Router 0xd83d960debec397fb149b51f8f37dd3b5cfa8913 decompiled source and ABI:
    • artifacts/root_cause/data_collector/iter_3/contract/1/0xd83d960debec397fb149b51f8f37dd3b5cfa8913/decompile.
  • Additional seed and trace artifacts for completeness:
    • WBTC, WETH9, and NonfungiblePositionManager source artifacts listed in artifacts/root_cause/seed/index.json.

These references allow a reader to reconstruct the transaction flows, verify the WBTC allowance and transfer semantics, and confirm the behavior of router 0xd83d960d...’s 0x67b34120 path that leads to the observed WBTC drain.