All incidents

Minimal-Proxy Pool Reinitializer Drain

Share
Nov 12, 2023 02:30 UTCAttackLoss: 3.49 ETH, 22,241.25 USDT +12 morePending manual check1 exploit txWindow: Atomic
Estimated Impact
3.49 ETH, 22,241.25 USDT +12 more
Label
Attack
Exploit Tx
1
Addresses
5
Attack Window
Atomic
Nov 12, 2023 02:30 UTC → Nov 12, 2023 02:30 UTC

Exploit Transactions

TX 1Ethereum
0x53eeab4447db331dbb47f93fd58a95d6faa230d559acde0687f8b5f5829e7a45
Nov 12, 2023 02:30 UTCExplorer

Victim Addresses

0x8dc0d817af40b1fa4f6b58034bb86f3bbefcb0a5Ethereum
0xb4ba49c919309ab66177ac7dece4d5cd6ef714e7Ethereum
0xa2473460f86e1058bdd0a2c531b15534fd403d97Ethereum
0xe2637e705475f367c94467c4b844d58db293aff8Ethereum
0xf5e303702b5927670998d6ec63449cb2edf65728Ethereum

Loss Breakdown

3.49ETH
22,241.25USDT
100,406.25RailToken
634,063.74BlockBank
234,364.3BUMPTokenV2
251,562.83HoprToken
43,444.44UnmarshalToken
1,466,613.44KelVpnERC20
12,753.72CellToken
52,414.87UnoRe
15,102.93Kine
7,364.4TXA
849,006.09MobiFi
124,730.33OddzToken

Similar Incidents

Root Cause Analysis

Minimal-Proxy Pool Reinitializer Drain

1. Incident Overview TL;DR

On Ethereum mainnet block 18552867, attacker EOA 0x4e087743e8025012c4704a1953c87eeff1e6ef48 executed transaction 0x53eeab4447db331dbb47f93fd58a95d6faa230d559acde0687f8b5f5829e7a45 through helper contract 0x3763b7f83358171b1660ee209f327954cc463129 and drained ETH plus multiple ERC20 balances from existing EIP-1167 pool clones created by factory 0x8dc0d817af40b1fa4f6b58034bb86f3bbefcb0a5. The exploit was permissionless: the attacker reconfigured already-live clones so that the clone storage treated attacker-controlled addresses as the pool admin and payout recipient, advanced the pool state through the normal lifecycle functions, then withdrew the full token or ETH balance from each targeted clone.

The root cause is an unrestricted reinitializer in implementation 0xb4ba49c919309ab66177ac7dece4d5cd6ef714e7. Selector 0xe7d25975 reaches the configuration routine without any sender authorization or one-time guard, so any caller can overwrite slots 0x02 and 0x03, mark arbitrary addresses as admins, and then invoke the privileged withdrawal path. The deterministic pre/post valuation artifact shows the attacker moved from 0.858005604156389841 ETH-equivalent before the exploit to 53.852561484079685125 ETH-equivalent after it, a post-fee increase of 52.994555879923295284 ETH-equivalent.

2. Key Background

The affected contracts are minimal proxies. Each pool clone delegates all logic to the same unverified implementation at 0xb4ba49c919309ab66177ac7dece4d5cd6ef714e7, so one implementation flaw is enough to expose every clone still pointing at it.

Three pieces of state matter for this incident:

  • The implementation stores admin authorization in a mapping keyed by address, and the decompiled helper func_2537 sets the admin flag for the supplied address.
  • Pool lifecycle state is packed into storage slot 0x03. The observed lifecycle in the incident is: completed pool before exploit, reset to attacker-controlled state by 0xe7d25975, transition via 0x4abe11b4, then transition via 0xd547557b into the withdrawal-enabled state.
  • Selector 0x90fb9dca transfers the full token balance of the selected ERC20, or the full native ETH balance if the token argument is zero, to the payout address encoded in slot 0x03.

The ACT pre-state is Ethereum mainnet immediately before block 18552867. At that point the affected clones still held stealable balances, still delegated to the vulnerable implementation, and still exposed the vulnerable public selectors. No private keys, privileged roles, or attacker-specific artifacts were required.

From the implementation decompilation, the vulnerable behavior can be summarized as:

// Decompiled behavior summarized from implementation 0xb4ba49...
function e7d25975(
    address slot2Value,
    address slot3Value,
    address token,
    address[] calldata admins,
    uint256 a,
    uint256 b,
    uint256 c,
    uint256 d,
    uint256 e
) external {
    slot2 = slot2Value;
    slot3 = slot3Value;
    admin[slot3Value] = true;
    for (uint256 i = 0; i < admins.length; ++i) {
        admin[admins[i]] = true;
    }
    // no msg.sender authorization check
    // no configure-once guard
}

3. Vulnerability Analysis & Root Cause Summary

This incident is an ATTACK, not a benign MEV event. The core safety invariant is that only the factory-designated owner or admin should be able to configure or reconfigure a pool clone, and privileged lifecycle and withdrawal functions must remain unreachable to arbitrary callers. The implementation breaks that invariant at the first step: selector 0xe7d25975 dispatches straight into the configuration routine and accepts attacker-controlled addresses and arrays without checking msg.sender or whether initialization has already happened. That routine overwrites the stored owner and payout recipient in slots 0x02 and 0x03, then marks attacker-supplied addresses as admins through the admin mapping write helpers. Once that storage corruption is complete, the later calls to 0x4abe11b4, 0xd547557b, and 0x90fb9dca are not the root cause; they are simply the intended admin-only path being executed with attacker-supplied admin state. The exploit is therefore deterministic: any unprivileged actor can repeat the same sequence against any clone that still points to the implementation and still holds assets.

The vulnerable components are:

  • Implementation 0xb4ba49c919309ab66177ac7dece4d5cd6ef714e7, selector 0xe7d25975, which serves as the unrestricted reinitializer.
  • The same implementation's admin write helpers (func_2537 and func_2B9C in the decompilation), which set attacker-supplied addresses as admins.
  • The same implementation's privileged state transition and withdrawal selectors 0x4abe11b4, 0xd547557b, and 0x90fb9dca, which become reachable after the attacker rewrites configuration.

The violated security principles are straightforward: missing configure-once protection, missing authorization on the configuration routine, and privileged asset withdrawal logic that trusted storage values any caller could rewrite through the configuration path.

4. Detailed Root Cause Analysis

The exploit begins with a live, already-configured clone. Representative victim clone 0xa2473460f86e1058bdd0a2c531b15534fd403d97 held 100406250000000000000000 RailToken units before the exploit and was in its completed pre-exploit state. The incident trace shows the attacker helper calling e7d25975 on that clone and immediately overwriting the key storage fields:

Representative seed-trace excerpt for clone 0xa2473460...
@ 11: 0 → 0x000000000000000000000000e76c6c83af64e4c60245d8c7de953df673a7a33d
@ 3:  0x0000000000000000000000043904efc39b16e9ce6483e8beac623fca370286d1
   →  0x0000000000000000000000003763b7f83358171b1660ee209f327954cc463129
@ 2:  0x0000000000000000000000001a17e083f272c9bb1d2e4d0d336047a27561ac4d
   →  0x0000000000000000000000003763b7f83358171b1660ee209f327954cc463129

That is the code-level breakpoint. An already-configured clone has its stored authority and payout fields replaced with attacker-controlled values. The same trace then shows the attacker walking the state machine into the withdrawal-enabled state:

Representative seed-trace excerpt after takeover
0xa2473460...::setPoolToClosed()
@ 3: 0x...003763b7f83358171b1660ee209f327954cc463129
 →   0x...013763b7f83358171b1660ee209f327954cc463129

0xa2473460...::d547557b()
@ 3: 0x...013763b7f83358171b1660ee209f327954cc463129
 →   0x...023763b7f83358171b1660ee209f327954cc463129

Once slot 0x03 encodes attacker-controlled authority plus pool state 2, the withdrawal selector can transfer out the entire victim balance. For the representative RailToken pool, the seed trace and balance diff agree that the full balance was removed from the clone:

{
  "token": "0xe76c6c83af64e4c60245d8c7de953df673a7a33d",
  "holder": "0xa2473460f86e1058bdd0a2c531b15534fd403d97",
  "before": "100406250000000000000000",
  "after": "0",
  "delta": "-100406250000000000000000"
}

The same pattern repeats across the other targeted clones in the same transaction. The clones at 0xe2637e705475f367c94467c4b844d58db293aff8 and 0xf5e303702b5927670998d6ec63449cb2edf65728 also show slot overwrites, state transitions, and balance-draining calls in the incident trace. This repetition matters because it rules out a one-off misconfiguration on a single pool; the attack uses the same implementation weakness against multiple clones in one bundle.

The exploit conditions are minimal:

  • A clone must still delegate to implementation 0xb4ba49c919309ab66177ac7dece4d5cd6ef714e7.
  • The clone must still hold ETH or ERC20 balances.
  • The attacker must be able to submit ordinary Ethereum transactions to the exposed selectors.

The profit calculation is now fully deterministic. The attacker EOA's native and WETH balances before and after the exploit are recorded in the profit valuation artifact:

{
  "native_before_wei": "858005604156389841",
  "native_after_wei": "4223956084156389841",
  "weth_before_wei": "0",
  "weth_after_wei": "49628605399923295284",
  "portfolio_before_in_eth": "0.858005604156389841",
  "portfolio_after_in_eth": "53.852561484079685125",
  "portfolio_delta_in_eth": "52.994555879923295284",
  "gas_fee_in_eth": "0.12659952"
}

5. Adversary Flow Analysis

The adversary strategy is a two-stage on-chain sequence.

First, the attacker EOA 0x4e087743e8025012c4704a1953c87eeff1e6ef48 deployed helper contract 0x3763b7f83358171b1660ee209f327954cc463129 in transaction 0xd2f3a9481395984736848c849140c6b86e34342be887aee3de2ae1639330109c at block 18552783. The helper is not privileged; it just packages the repeated clone interactions into one transaction.

Second, the attacker used exploit transaction 0x53eeab4447db331dbb47f93fd58a95d6faa230d559acde0687f8b5f5829e7a45 at block 18552867 to:

  1. Call 0xe7d25975 on each target clone with attacker-controlled owner, recipient, token, and admin array values.
  2. Call 0x4abe11b4 and 0xd547557b to move the clone into the withdrawal-enabled state.
  3. Call 0x90fb9dca(token,false) or the ETH path to transfer out the full victim balance.
  4. Swap stolen ERC20 balances into WETH and send the WETH to the attacker EOA.

The seed metadata ties the exploit directly to the attacker EOA and helper:

from: 0x4e087743e8025012c4704a1953c87eeff1e6ef48
to:   0x3763b7f83358171b1660ee209f327954cc463129
hash: 0x53eeab4447db331dbb47f93fd58a95d6faa230d559acde0687f8b5f5829e7a45

The monetization step is visible in the trace as repeated WETH transfers to the attacker EOA. Representative examples from the same exploit transaction include:

WETH9::transfer(0x4E087743E8025012C4704a1953C87eeFF1e6EF48, 13333448332679721570)
WETH9::transfer(0x4E087743E8025012C4704a1953C87eeFF1e6EF48, 10260606491788421960)
WETH9::transfer(0x4E087743E8025012C4704a1953C87eeFF1e6EF48, 6113149077967615156)

The identified adversary cluster is:

  • EOA 0x4e087743e8025012c4704a1953c87eeff1e6ef48, which deployed the helper and sent the exploit transaction.
  • Helper contract 0x3763b7f83358171b1660ee209f327954cc463129, which executed the repeated clone interactions.

The public victim-side components observed in the incident are:

  • Factory 0x8dc0d817af40b1fa4f6b58034bb86f3bbefcb0a5
  • Implementation 0xb4ba49c919309ab66177ac7dece4d5cd6ef714e7
  • Representative clones 0xa2473460f86e1058bdd0a2c531b15534fd403d97, 0xe2637e705475f367c94467c4b844d58db293aff8, and 0xf5e303702b5927670998d6ec63449cb2edf65728

6. Impact & Losses

The impact is direct pool theft. Existing clones were reconfigured and drained without any legitimate admin action. The incident balance diff and root cause artifact record the following stolen inventory:

  • ETH: 3492550000000000000 (decimals: 18)
  • USDT: 22241250771 (decimals: 6)
  • RailToken: 100406250000000000000000 (decimals: 18)
  • BlockBank: 634063738278029040890177 (decimals: 18)
  • BUMPTokenV2: 234364304996350508376501 (decimals: 18)
  • HoprToken: 251562826931405333333335 (decimals: 18)
  • UnmarshalToken: 43444444474768518510625 (decimals: 18)
  • KelVpnERC20: 1466613438727083096939103 (decimals: 18)
  • CellToken: 12753722857142857142885 (decimals: 18)
  • UnoRe: 52414871798928571428578 (decimals: 18)
  • Kine: 15102930000000000000003 (decimals: 18)
  • TXA: 7364395879548000000000 (decimals: 18)
  • MobiFi: 849006087810833333333340 (decimals: 18)
  • OddzToken: 124730325948563194106632 (decimals: 18)

The attacker's realized post-fee portfolio increase was 52.994555879923295284 ETH-equivalent. The seed balance diff independently confirms the native ETH increase of 3365950480000000000 wei for the attacker EOA, while the trace and profit valuation artifact account for the WETH received from liquidating the stolen ERC20 inventory.

7. References

Relevant transactions:

  • Chain 1: 0xd2f3a9481395984736848c849140c6b86e34342be887aee3de2ae1639330109c (helper deployment)
  • Chain 1: 0x53eeab4447db331dbb47f93fd58a95d6faa230d559acde0687f8b5f5829e7a45 (seed exploit transaction)

Evidence sources:

  • [1] Seed metadata: /workspace/session/artifacts/collector/seed/1/0x53eeab4447db331dbb47f93fd58a95d6faa230d559acde0687f8b5f5829e7a45/metadata.json
  • [2] Seed trace: /workspace/session/artifacts/collector/seed/1/0x53eeab4447db331dbb47f93fd58a95d6faa230d559acde0687f8b5f5829e7a45/trace.cast.log
  • [3] Seed balance diff: /workspace/session/artifacts/collector/seed/1/0x53eeab4447db331dbb47f93fd58a95d6faa230d559acde0687f8b5f5829e7a45/balance_diff.json
  • [4] Implementation decompilation: https://ethervm.io/decompile/0xb4ba49c919309ab66177ac7dece4d5cd6ef714e7
  • [5] Representative clone code record: https://etherscan.io/address/0xa2473460f86e1058bdd0a2c531b15534fd403d97
  • [6] Helper creation record: https://etherscan.io/address/0x3763b7f83358171b1660ee209f327954cc463129
  • [7] Deterministic profit valuation: /workspace/session/artifacts/auditor/iter_1/profit_valuation.json