All incidents

RubicProxy USDC Drain

Share
Dec 25, 2022 08:31 UTCAttackLoss: 1,475,491.81 USDCPending manual check1 exploit txWindow: Atomic
Estimated Impact
1,475,491.81 USDC
Label
Attack
Exploit Tx
1
Addresses
2
Attack Window
Atomic
Dec 25, 2022 08:31 UTC → Dec 25, 2022 08:31 UTC

Exploit Transactions

TX 1Ethereum
0x9a97d85642f956ad7a6b852cf7bed6f9669e2c2815f3279855acf7f1328e7d46
Dec 25, 2022 08:31 UTCExplorer

Victim Addresses

0x3335a88bb18fd3b6824b59af62b50ce494143333Ethereum
0x33388cf69e032c6f60a420b37e44b1f5443d3333Ethereum

Loss Breakdown

1,475,491.81USDC

Similar Incidents

Root Cause Analysis

RubicProxy USDC Drain

1. Incident Overview TL;DR

At Ethereum block 16260581, EOA 0x001b91c794dfeecf00124d3f9525dd32870b6ee9 called helper contract 0x253dd81d642220267ccac1d8202c0b96a92b299e, which drained USDC from 26 externally owned accounts that had previously approved RubicProxy contracts 0x3335a88bb18fd3b6824b59af62b50ce494143333 and 0x33388cf69e032c6f60a420b37e44b1f5443d3333 as spenders. The attacker then sold the stolen 1475491811413 USDC through Uniswap V2 for 1161550285917089507094 wei and realized 1161498756517089507094 wei net after gas.

The root cause was not stolen victim keys or a privileged integration path. The issue was that RubicProxy exposed a permissionless routerCallNative entrypoint that accepted arbitrary calldata for a whitelisted router target, and the router whitelist admitted the USDC token contract itself. Because the proxies were already approved spenders, arbitrary callers could make them execute USDC.transferFrom(victim, recipient, amount).

2. Key Background

USDC follows the standard allowance model: transferFrom(from, to, value) succeeds when allowed[from][msg.sender] >= value, then decrements that allowance. In this incident, the crucial spender identity was the RubicProxy contract, not the attacker EOA.

RubicProxy acted as a public routing surface. The analysis shows that its routerCallNative path checked whether the supplied router address was whitelisted, then forwarded attacker-controlled calldata to that router. If the whitelisted router is an ERC20 token contract, the forwarded calldata can be an arbitrary token method such as transferFrom.

The exploit therefore required only public preconditions: victims with USDC balances, existing USDC approvals to the vulnerable proxies, and a proxy whitelist that still admitted USDC as a router target.

3. Vulnerability Analysis & Root Cause Summary

This was an ATTACK-class arbitrary-call exposure through a public approved-spender proxy. The safety invariant is straightforward: a public routing proxy that holds ERC20 spender approvals must not let arbitrary callers choose calldata that can move third-party funds. RubicProxy violated that invariant by exposing routerCallNative without caller authorization while allowing a token contract to appear in the router allowlist. Once _params.router was set to USDC, the proxy became a generic USDC.transferFrom executor for any approved victim. The exploit path was deterministic because the victims had already granted allowance to the proxy contracts, and USDC enforced allowance only against msg.sender, which was the proxy. The attack completed in a single transaction, so there was no dependency on private orderflow, victim interaction during execution, or privileged roles.

4. Detailed Root Cause Analysis

The seed transaction 0x9a97d85642f956ad7a6b852cf7bed6f9669e2c2815f3279855acf7f1328e7d46 called helper contract 0x253dd81d642220267ccac1d8202c0b96a92b299e, which repeatedly invoked both vulnerable proxies with router = USDC and calldata encoding transferFrom(victim, helper, amount). The on-chain trace shows the exact pattern:

0x33388CF69e032C6f60A420b37E44b1F5443d3333::routerCallNative(
  "",
  (..., integrator=0x677d6EC74fA352D4Ef9B1886F6155384aCD70D90, router=0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48),
  0x23b872dd...915e8832...253dd81d...19626ef55a
)
0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48::transferFrom(
  0x915E88322EDFa596d29BdF163b5197c53cDB1A68,
  0x253dD81d642220267ccAc1d8202c0B96a92b299e,
  109025621338
)

The token-side enforcement is visible in USDC's source:

function transferFrom(address from, address to, uint256 value) external returns (bool) {
    require(value <= allowed[from][msg.sender], "ERC20: transfer amount exceeds allowance");
    _transfer(from, to, value);
    allowed[from][msg.sender] = allowed[from][msg.sender].sub(value);
    return true;
}

That code explains why the exploit worked. msg.sender inside USDC was the RubicProxy contract, and the victims had already approved that proxy. The trace also shows balance and allowance reads immediately before each drain, followed by successful Transfer events and storage updates that zeroed victim balances or reduced allowances. The helper contract aggregated the stolen tokens, approved Uniswap V2 Router02, and swapped the entire USDC balance for ETH in the same transaction.

5. Adversary Flow Analysis

The adversary flow had three deterministic stages. First, the attacker EOA submitted the exploit transaction to helper contract 0x253dd81d642220267ccac1d8202c0b96a92b299e. Second, that helper iterated through victim addresses and, depending on which proxy each victim had approved, invoked either proxy one or proxy two with calldata for USDC.transferFrom(victim, helper, amount). Third, after the helper accumulated 1475491811413 USDC, it approved Uniswap V2 Router02 and executed swapExactTokensForETH, delivering ETH back to the attacker EOA.

The liquidation leg is also explicit in the trace:

0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D::swapExactTokensForETH(
  1475491811413,
  0,
  [USDC, WETH],
  0x001B91c794dFEecf00124D3F9525DD32870B6ee9,
  1671957071
)

Nothing in that sequence required privileged credentials. The helper contract was operational convenience, not an access prerequisite. A fresh unprivileged caller can reproduce the same effect on a mainnet fork by directly calling the public proxy functions against publicly observable approved victims.

6. Impact & Losses

The measurable asset loss was 1475491811413 raw USDC units, or 1,475,491.811413 USDC at 6 decimals. Twenty-six victim EOAs had their approved USDC balances drained. The post-drain liquidation yielded 1161550285917089507094 wei to the attacker before gas, and the balance diff records a net native-asset gain of 1161498756517089507094 wei after gas costs.

The impact was immediate and irreversible within the transaction because the stolen stablecoins were converted to ETH in the same execution path.

7. References

  • Seed transaction metadata: 0x9a97d85642f956ad7a6b852cf7bed6f9669e2c2815f3279855acf7f1328e7d46
  • Seed trace showing proxy calls, token drains, and swap: trace.cast.log for the same transaction
  • Balance diff showing victim losses and attacker profit: balance_diff.json for the same transaction
  • Vulnerable proxies: 0x3335a88bb18fd3b6824b59af62b50ce494143333, 0x33388cf69e032c6f60a420b37e44b1f5443d3333
  • Token contract: USDC 0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48
  • DEX liquidation path: Uniswap V2 Router02 0x7a250d5630b4cf539739df2c5dacb4c659f2488d, pair 0xB4e16d0168e52d35CaCD2c6185b44281Ec28C9Dc