WETH Drain via Unprotected 0xfa461e33 Callback on 0x03f9-62c0
Exploit Transactions
0x1a3e9eb5e00f39e84f90ca23bd851aa194b1e7a90003e3f6b9b17bbb66dabbb9Victim Addresses
0x03f911aedc25c770e701b8f563e8102cfacd62c0EthereumLoss Breakdown
Similar Incidents
Unauthorized WETH drain via unprotected Uniswap V3 callback
52%AnyswapV4Router WETH9 permit misuse drains WETH to ETH
39%GPv2Settlement allowance leak lets router drain WETH and USDC
38%ORAAI/BUBAI backdoored tokens drain WETH from LP pools
36%WBTC Drain via Insecure Router transferFrom Path
36%FlippazOne ungated ownerWithdrawAllTo lets attacker drain 1.15 ETH
36%Root Cause Analysis
WETH Drain via Unprotected 0xfa461e33 Callback on 0x03f9-62c0
1. Incident Overview TL;DR
On Ethereum mainnet at block 20737849, an unprivileged adversary EOA 0xf073a21f0d68adacfff34d5b8df04550c944e348 sent a single transaction 0x1a3e9eb5e00f39e84f90ca23bd851aa194b1e7a90003e3f6b9b17bbb66dabbb9 to an adversary-controlled router contract 0xd683b81c2608980db90a6fd730153e04629ff1a3. The router invoked a publicly callable callback-style function with selector 0xfa461e33 on contract 0x03f911aedc25c770e701b8f563e8102cfacd62c0, causing that contract to transfer its entire WETH balance (737035470365687848 wei, i.e., 0.737035470365687848 WETH) to the router. The router immediately unwrapped this WETH to ETH via WETH9.withdraw and paid 0.375802068345754422 ETH to the block coinbase 0x4838b106fce9647bdf1e7877bf73ce8b0bad5f97 and 0.361233402019933426 ETH to the EOA, yielding a net profit of 0.361095696049195426 ETH for the adversary after gas.
The root cause is a missing access-control check on the 0xfa461e33 callback entrypoint of 0x03f9...62c0. This function, intended to behave like a Uniswap V3-style callback, unconditionally builds and executes a WETH.transfer(recipient, amount) using token, recipient, and amount values derived entirely from attacker-controlled calldata, without authenticating msg.sender as a trusted pool or contract. As long as 0x03f9...62c0 holds WETH and the calldata passes local bounds checks, any adversary-controlled router can call this function and drain the contract’s WETH in a single transaction. The incident satisfies the ACT criteria: a permissionless adversary cluster can reproduce the exploit using only public on-chain data, canonical contract metadata, and standard transaction inclusion rules.
2. Key Background
WETH9 at 0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2 is the canonical wrapped ETH token on Ethereum mainnet, with standard ERC‑20 semantics and a withdraw(uint256) function that burns WETH and releases the same amount of ETH. Verified source from Etherscan confirms standard transfer, Transfer and Withdrawal events, and 18‑decimal accounting for balances and amounts.
The victim contract 0x03f911aedc25c770e701b8f563e8102cfacd62c0 is a WETH‑holding helper that exposes a function with selector 0xfa461e33 and runtime patterns matching uniswapV3SwapCallback(int256,int256,bytes). Disassembly and decompilation show that this entrypoint decodes (amount0Delta, amount1Delta, bytes data) from calldata, checks a token address against the hard‑coded WETH9 address, and then performs a WETH.transfer using a recipient and amount derived from the decoded data. The contract’s logic is largely stateless: it does not maintain per‑user accounting or pool configuration in storage and does not gate the callback on msg.sender.
The adversary router 0xd683b81c2608980db90a6fd730153e04629ff1a3 is a route execution contract tightly bound to EOA 0xf073...348. Decompilation shows multiple entrypoints guarded by require(tx.origin == 0xf073...348) and similar CALLER checks, and route‑decoding logic that builds downstream calls into other contracts, including the victim’s 0xfa461e33 entrypoint. The router also contains logic to call WETH9.withdraw and forward ETH to profit addresses.
Under the ACT model, the relevant system is the on‑chain triad of the WETH token, the WETH‑holding helper contract 0x03f9...62c0, and the adversary router/EOA cluster. All information needed to reproduce the exploit—addresses, function selectors, calldata shapes, balances, and traces—is available via public RPCs, logs, and verified contract metadata. Upstream lifecycle details (e.g., who originally deposited WETH into 0x03f9...62c0 or the router’s deployment transaction) are not needed to realize the exploit and are treated explicitly as limitations due to data‑provider constraints.
3. Vulnerability Analysis & Root Cause Summary
This incident is categorized as an ATTACK: an unprivileged adversary exploits a concrete code‑level bug in an on‑chain contract to drain funds.
The vulnerability is an unprotected callback‑style entrypoint on 0x03f9...62c0 (selector 0xfa461e33) that performs value‑moving operations (WETH transfers) based solely on attacker‑controlled calldata. The function does not authenticate msg.sender against a trusted DEX pool or any other allowlist and relies on untrusted bytes data to supply both the WETH token address and the recipient address. As a result, any contract can call this function and cause the victim to transfer arbitrary amounts of WETH to arbitrary recipients, as long as the contract’s balance is sufficient and the local WETH address check passes.
The intended invariant is that the victim’s WETH‑settling callback should only be invoked by specific, trusted AMM pools or protocol contracts in the context of legitimate swaps or position settlements. The code, however, enforces no such relationship and allows pure external callers to trigger WETH outflows without any compensating inflow or pricing logic, making the function an open‑drain primitive.
The concrete breakpoint is the first WETH.transfer(recipient, amount) issued by 0x03f9...62c0 during the 0xfa461e33 execution path, where recipient and amount are derived from attacker‑controlled calldata and no check is made that msg.sender is a trusted pool. In the exploit transaction, recipient is set to the adversary router 0xd683...1a3 and amount is set to the victim’s full WETH balance.
4. Detailed Root Cause Analysis
At pre‑state σ_B (immediately before block 20737849), canonical WETH balance queries show:
// WETH balances around σ_B (before/after seed tx)
{
"weth": "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2",
"victim": "0x03f911aedc25c770e701b8f563e8102cfacd62c0",
"router": "0xd683b81c2608980db90a6fd730153e04629ff1a3",
"balances": {
"before": {
"victim_wei": 737035470365687848,
"router_wei": 1
},
"after": {
"victim_wei": 0,
"router_wei": 1
}
},
"allowances": {
"before": { "allowance_wei": 0 },
"after": { "allowance_wei": 0 }
}
}
There is no WETH allowance from the victim to the router, so any movement of WETH from 0x03f9...62c0 to 0xd683...1a3 must be initiated by the victim contract itself.
The seed transaction is a type‑2 EOA transaction from 0xf073...348 to 0xd683...1a3 with zero ETH value and calldata beginning with selector 0x000000c5. The router decodes this calldata as a single route step that targets 0x03f9...62c0 with selector 0xfa461e33 and encodes:
token = 0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2(WETH9),recipient = 0xd683b81c2608980db90a6fd730153e04629ff1a3,amount = 737035470365687848.
The callTracer trace of the transaction shows the resulting call chain:
// Seed tx callTracer excerpt (tx 0x1a3e9e...bbb9)
{
"calls": [
{
"from": "0xd683b81c2608980db90a6fd730153e04629ff1a3",
"to": "0x03f911aedc25c770e701b8f563e8102cfacd62c0",
"input": "0xfa461e33...c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2...d683b81c2608980db90a6fd730153e04629ff1a3...",
"type": "CALL",
"calls": [
{
"from": "0x03f911aedc25c770e701b8f563e8102cfacd62c0",
"to": "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2",
"input": "0xa9059cbb000000000000000000000000d683b81c2608980db90a6fd730153e04629ff1a30000000000000000000000000000000000000000000000000a3a79daf6028028",
"type": "CALL"
}
]
}
]
}
The inner call from 0x03f9...62c0 to 0xc02a...56cc2 with selector 0xa9059cbb is WETH.transfer(0xd683...1a3, 737035470365687848). This confirms that the victim contract itself transfers all of its WETH to the router during the 0xfa461e33 execution.
Disassembly and decompilation of the victim’s runtime bytecode show that the 0xfa461e33 entrypoint decodes its arguments, passes them into a helper that checks the token address against a hard‑coded WETH constant, and then constructs the WETH.transfer calldata. Crucially, the helper and the caller perform no checks on msg.sender: there is no storage‑backed whitelist of allowed pools, and the only CALLER opcodes in the relevant region are used for data forwarding, not for access control.
On the router side, decompilation of 0xd683...1a3 further confirms adversary control and the intended flow:
// Router 0xd683...1a3, key callback helper (decompiled)
/// @custom:selector 0xfa461e33
function Unresolved_fa461e33(uint256 arg0, uint256 arg1, uint256 arg2, uint256 arg3) public {
require(!arg2 > 0xffffffffffffffff);
require(!(arg2) > 0xffffffffffffffff);
require(0xf073a21f0d68adacfff34d5b8df04550c944e348 == tx.origin);
...
address recipient = address(msg.sender);
uint256 amount = arg0;
require(address(arg2 + 0x20).code.length);
// effectively token.transfer(recipient, amount)
(bool success, bytes memory ret0) = address(arg2 + 0x20).Unresolved_a9059cbb(recipient);
...
}
Here, arg2 encodes the token address region, and arg2 + 0x20 points to the token address chosen by the route (WETH9 in the incident). The router enforces tx.origin == 0xf073...348 but does not restrict downstream targets—so as long as the victim’s callback is callable, the router can direct the victim to transfer tokens to itself.
The end‑to‑end effect in the seed transaction is:
- EOA
0xf073...348calls router0xd683...1a3with a crafted route step targeting0x03f9...62c0::0xfa461e33. - Router calls
0x03f9...62c0::0xfa461e33withtoken=WETH9,recipient=0xd683...1a3,amount=737035470365687848. - Victim
0x03f9...62c0executesWETH.transfer(0xd683...1a3, 737035470365687848)without checkingmsg.sender. - Router calls
WETH9.withdraw(737035470365687848), receives ETH, and forwards ETH to the coinbase and the EOA.
Native balance diffs corroborate the value flows:
// Native ETH balance diffs for seed tx
{
"native_balance_deltas": [
{
"address": "0x4838b106fce9647bdf1e7877bf73ce8b0bad5f97",
"delta_wei": "375802068345754422"
},
{
"address": "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2",
"delta_wei": "-737035470365687848"
},
{
"address": "0xf073a21f0d68adacfff34d5b8df04550c944e348",
"delta_wei": "361095696049195426"
}
]
}
WETH9’s ETH backing decreases by exactly the amount of WETH drained from the victim, and the EOA’s net ETH increase (after gas) is 0.361095696049195426 ETH.
This sequence violates the invariant that only designated pools or protocol contracts may invoke the victim’s WETH‑settling callback to decrease its WETH balance in the context of legitimate swaps. Because the callback is fully public and trusts attacker‑supplied token/recipient/amount values, it can be exploited by any adversary‑controlled router, making this an ACT‑style, permissionless value‑drain vulnerability.
5. Adversary Flow Analysis
Adversary strategy. The adversary uses a single‑transaction bug‑based drain: a router under their exclusive control calls an unprotected callback on a WETH‑holding helper contract, causing the helper to self‑transfer all its WETH to the router, which then unwraps to ETH and pays out profit to the EOA and a large bribe to the block coinbase.
Adversary‑related cluster accounts.
- EOA
0xf073a21f0d68adacfff34d5b8df04550c944e348(chainid 1) is the sender andtx.originof the exploit transaction, and its native balance increases by 0.361095696049195426 ETH after gas. Router decompilation shows hard‑codedtx.originchecks equating to this address, confirming that the EOA controls the router and is the ultimate profit beneficiary. - Contract
0xd683b81c2608980db90a6fd730153e04629ff1a3(chainid 1) is an adversary‑owned router/orchestrator. It is the direct callee of the seed transaction, initiates the internal call into0x03f9...62c0::0xfa461e33, receives drained WETH and the resulting ETH fromWETH.withdraw, and distributes ETH to the EOA and the block coinbase.
Victim system. The immediate on‑chain victim system is the WETH‑holding helper contract 0x03f9...62c0 on Ethereum mainnet. It is treated as a WETH callback helper in the protocol naming fields and has verification status unknown because contract‑verification metadata could not be retrieved from available APIs. From the ACT perspective, this contract and its WETH balance at σ_B are the assets at risk.
Lifecycle stages.
-
Adversary router deployment (upstream, not reconstructed). At some time before σ_B, router
0xd683...1a3was deployed and wired to EOA0xf073...348via hard‑codedtx.originandmsg.senderchecks. Attempts to retrieve address‑level txlists or contract‑creator metadata for this contract via Etherscan v2 and QuickNode failed with HTTP 403 or “method not found”, so the precise deployment transaction is not known from current artifacts. This upstream gap is documented as a limitation and does not affect the exploitability of the victim’s callback. -
Victim WETH position existing at σ_B. Immediately before block 20737849, WETH balances show
WETH9.balanceOf(0x03f9...62c0) = 737035470365687848andWETH9.balanceOf(0xd683...1a3) = 1, withWETH9.allowance(0x03f9...62c0, 0xd683...1a3) = 0. Extensive WETH transfer logs and partial traces in iter_3 confirm that0x03f9...62c0is a long‑lived WETH‑holding helper interacting with many counterparties, but the full upstream funding path for this exact WETH balance is not reconstructible given current provider limits. -
Exploit transaction (drain via callback). In block 20737849, transaction
0x1a3e9e...bbb9executes the route step described above. The router calls the victim’s0xfa461e33callback with crafted calldata, causing the victim to transfer its entire WETH balance to the router. The router then callsWETH9.withdraw(737035470365687848)and forwards ETH to the coinbase and EOA. After the transaction,WETH9.balanceOf(0x03f9...62c0) = 0andWETH9.balanceOf(0xd683...1a3)returns to 1, confirming that all WETH previously held by the victim has been drained and unwrapped.
From an ACT perspective, this sequence is fully reproducible by any adversary cluster that can deploy a router and control an EOA: they need only replicate the router logic that calls 0x03f9...62c0::0xfa461e33 with token=WETH9, recipient=router, and amount equal to the victim’s WETH balance, followed by WETH.withdraw and profit distribution.
6. Impact & Losses
The immediate on‑chain impact is the loss of 0.737035470365687848 WETH held by 0x03f9...62c0 at σ_B. This WETH is transferred to the adversary router and unwrapped to ETH, reducing WETH9’s ETH backing by the same amount.
Balance diffs show:
- WETH9’s ETH balance decreases by 737035470365687848 wei.
- The block coinbase
0x4838b106fce9647bdf1e7877bf73ce8b0bad5f97gains 0.375802068345754422 ETH as a block‑level bribe. - The adversary EOA
0xf073...348gains 0.361233402019933426 ETH from the router and pays 0.000137705970738 ETH in gas, for a net profit of 0.361095696049195426 ETH.
The ultimate economic bearers of this loss (e.g., which upstream protocol, LPs, or users’ capital is aggregated in 0x03f9...62c0) cannot be uniquely identified from the available artifacts. WETH transfer logs involving 0x03f9...62c0 span 215,542 events across 97,863 distinct transactions, and full txlists for the victim and router addresses are unavailable due to provider/API restrictions. These upstream lifecycle uncertainties are explicitly recorded as limitations but do not affect the characterization of the immediate ACT opportunity or the victim contract‑level loss.
7. References
- Seed transaction trace and metadata for tx
0x1a3e9eb5e00f39e84f90ca23bd851aa194b1e7a90003e3f6b9b17bbb66dabbb9(QuickNode RPC callTracer and receipt JSON). - Native balance diffs for the seed transaction (pre‑/post‑state ETH balances for coinbase, WETH9, and the adversary EOA).
- WETH balances and allowances around the seed transaction, including σ_B (
weth_balances_and_allowances.jsonfor victim and router). - Victim contract
0x03f911aedc25c770e701b8f563e8102cfacd62c0runtime bytecode, disassembly, and decompilation (iter_2 contract artifacts). - Router contract
0xd683b81c2608980db90a6fd730153e04629ff1a3runtime bytecode, disassembly, and decompilation (iter_2 contract artifacts). - WETH9 (
0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2) verified source and ABI (Etherscan getsourcecode output). - Iter_3 data collection summary and WETH transfer log index documenting provider limitations and the scale of upstream WETH transfer activity involving
0x03f9...62c0.