WBTC Drain via Insecure Router transferFrom Path
Exploit Transactions
0x8f28a7f604f1b3890c2275eec54cd7deb40935183a856074c0a06e4b5f72f25aVictim Addresses
0x5240b03be5bc101a0082074666dd89ad883e1f9dEthereumLoss Breakdown
Similar Incidents
WETH9 Balance Drain via Router 0x5697-1751 Using Victim
40%GPv2Settlement allowance leak lets router drain WETH and USDC
39%SilicaPools decimal-manipulation bug drains WBTC flashloan collateral
36%WETH Drain via Unprotected 0xfa461e33 Callback on 0x03f9-62c0
36%LiFi GasZipFacet / LibSwap arbitrary USDT transferFrom
36%Hegic WBTC Pool Repeated Tranche Withdrawal Exploit
36%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
0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2and Uniswap V3 NonfungiblePositionManager0xc36442b4a4522e871399cd717abdd847ab11fe88, which receive liquidity and mint LP positions. - Adversary EOA
0xe3e73f1e6ace2b27891d41369919e8f57129e8eaand helper contract0x5c92884dfe0795db5ee095e68414d6aabf398130.
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
CALLto 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 inT.transferFrom(v, r, a), the router must ensure thatveither (i) ismsg.senderat the router entry, or (ii) has provided a verifiable authorization (for example, a signature verified viaecrecoveror a Permit2-style approval) that bindsvto 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 router0xd83d960d....
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
CALLto 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(thecontractAddressfield). - Sends a call from the helper to router
0xd83d960d...with selector0x67b34120.
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, or36.91897652WBTC 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
Transferevent:
// WBTC Transfer log for tx 0x8f28a7f...
{
"address": "0x2260fac5e5542a773aa44fbcfedf7c193bc2c599",
"topics": [
"0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
"0x0000000000000000000000005240b03be5bc101a0082074666dd89ad883e1f9d",
"0x000000000000000000000000e3e73f1e6ace2b27891d41369919e8f57129e8ea"
],
"data": "0x00000000000000000000000000000000000000000000000000000000dc0de334"
}
- A mint of Uniswap V3 LP NFT
tokenId 0x1205bato helper contract0x5c9288...from NonfungiblePositionManager0xc36442b4.... - A small WETH deposit from the helper into the Uniswap pool.
The balance_diff.json confirms that:
- The adversary EOA spends approximately
0.001463917118489758ETH in gas and commits100wei 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:
- Rely on a victim EOA that has granted an unlimited WBTC allowance to router
0xd83d960d...and is using it for normal routing. - Deploy a helper contract and call router function
0x67b34120with 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.91897652WBTC in the WBTCTransferevent. - Funder and deployer of helper contract
0x5c9288....
- Sender of attacker-crafted tx
0x5c92884dfe0795db5ee095e68414d6aabf398130(contract):- Deployed by
0xe3e73f1e...in tx0x8f28a7f.... - Calls router
0xd83d960d...’s0x67b34120function. - Receives Uniswap V3 LP NFT
tokenId 0x1205ba, which embeds the stolen WBTC value.
- Deployed by
Victim-related entities
- Victim EOA
0x5240b03be5bc101a0082074666dd89ad883e1f9d, holder of the drained WBTC. - WBTC token
0x2260fac5e5542a773aa44fbcfedf7c193bc2c599. - Router contract
0xd83d960debec397fb149b51f8f37dd3b5cfa8913.
Lifecycle stages
-
Victim allowance setup and router usage
- Tx:
0x43aa58c764c762b65a86e6e343ee575fd163c8ff983f778fb693d9c9874be85c(block24285462). - Mechanism:
approve. - Effect: Victim EOA grants router
0xd83d960d...an effectively unlimited WBTC allowance on0x2260fac5.... This is a standard user action and not an error by itself, but it enables the router to callWBTC.transferFromon the victim’s behalf.
- Tx:
-
Adversary helper contract deployment
- Tx:
0x8f28a7f604f1b3890c2275eec54cd7deb40935183a856074c0a06e4b5f72f25a(block24313234). - Mechanism: contract deployment from adversary EOA.
- Effect:
0xe3e73f1e...deploys helper contract0x5c9288..., funding it with100wei. The helper is designed to call router0xd83d960d...’s0x67b34120function with crafted parameters.
- Tx:
-
Adversary exploit execution and profit realization
- Tx: same
0x8f28a7f...includes the exploit call. - Mechanism: helper calls router
0xd83d960d...(router_call) with0x67b34120and embedded0x23b872ddpayload. - Effect: Router executes
WBTC.transferFrom(0x5240b03b..., 0xe3e73f1e..., 36.91897652 WBTC)and then mints Uniswap V3 LP NFTtokenId 0x1205bato helper0x5c9288..., 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.
- Tx: same
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.91897652WBTC. - Victim: EOA
0x5240b03be5bc101a0082074666dd89ad883e1f9d. - Adversary: EOA
0xe3e73f1e6ace2b27891d41369919e8f57129e8eaand helper contract0x5c92884dfe0795db5ee095e68414d6aabf398130.
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
0xd83d960debec397fb149b51f8f37dd3b5cfa8913decompiled 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.
- WBTC, WETH9, and NonfungiblePositionManager source artifacts listed in
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.