All incidents

LiteV3 Bridge Aggregator Proxy Initialization Race Enabled Unauthorized UUPS Takeover

Share
Feb 11, 2026 14:55 UTCAttackLoss: No Financial LossManually checked1 exploit txWindow: Atomic

Root Cause Analysis

LiteV3 Bridge Aggregator Proxy Initialization Race Enabled Unauthorized UUPS Takeover

1. Incident Overview TL;DR

On Ethereum mainnet, LiteV3 Bridge Aggregator proxy 0x3f568ac2bb7a7099a243d11419185c922cfab766 was upgraded in tx 0xe5b892256096f0771e69e60c55ed29170839dcaba95c37f85de10d04acf8fd94 (block 24434263) to implementation 0x0e31530ea382ea2cde14cbff0f49110865fc7d5e without same-transaction initialization. In the open window before trusted initialization, an adversary-controlled flow sent tx 0x109274c9a495df5083712800bd0771a02906c1f5986d0bd06c904c667bb511d4 (block 24434264) through Multicall3 and helper contract 0x04202d7ec47587562e622a2f4f1fbc7fac588473 to call proxy initialization and upgrade paths. That transaction emitted OwnershipTransferred(0x0 -> 0x04202d7e...) and switched the EIP-1967 implementation slot from 0x0e31530e... to 0x9a4400ea....

Root cause: initialization and authority setup were not atomic with implementation activation, while the implementation exposed an externally callable initialize(address) reachable through the proxy while uninitialized. This created a deterministic ACT window where any unprivileged actor could seize control-critical state.

2. Key Background

The victim proxy is a standard ERC1967 proxy wrapper and delegates logic to the implementation address stored in the EIP-1967 slot.

contract LiteV3BridgeAggregatorProxy is ERC1967Proxy {
    constructor(address logic_, bytes memory data_) payable ERC1967Proxy(logic_, data_) {}
}

The implementation code defines authority setup in initialize(address) and upgrade authorization in _authorizeUpgrade:

function initialize(address owner_) external initializer validateAddress(owner_) {
    _transferOwnership(owner_);
    _status = 1;
}

function _authorizeUpgrade(address) internal override onlyOwner {}

This means first successful initialization fixes owner/upgrade authority in proxy storage. If upgrade and initialization are separated, any external caller can race to initialize first while initializer is still open.

The observed transaction timeline on proxy address confirms this sequencing:

[
  {
    "blockNumber": "24434263",
    "hash": "0xe5b892256096f0771e69e60c55ed29170839dcaba95c37f85de10d04acf8fd94",
    "functionName": "upgradeTo(address imp)"
  },
  {
    "blockNumber": "24434305",
    "hash": "0x61af0d602cc251d7fb6faee8124fc3064536c4683622c9eaf3dd747674038e3a",
    "functionName": "initialize(address eos)"
  }
]

3. Vulnerability Analysis & Root Cause Summary

This is an access-control takeover in an upgradeability lifecycle window (root_cause_category = ATTACK), not a pure MEV price dislocation. The vulnerable pattern is operational: implementation activation (upgradeTo) and trusted initialization were split into separate transactions. At code level, initialize(address) is externally callable and only protected by one-time initializer state, so first caller during the open window can set ownership. Because UUPS upgrades rely on owner-gated authorization, first ownership assignment determines future upgrade authority. The adversary exploited exactly this ordering gap and executed an unauthorized upgrade path before trusted finalization. The resulting system state broke the intended invariant that only the trusted operator establishes initial authority after implementation activation.

Exploit preconditions that were all satisfied:

  • Proxy pointed to an implementation exposing external initializer while owner/authority was unset.
  • Initialization was not finalized atomically with implementation activation.
  • Adversary could submit a transaction before operator initialization.

Security principles violated:

  • Atomic initialization on upgradeable deployments.
  • Fail-safe default privilege assignment.
  • Minimization of externally reachable privileged setup functions during live migration windows.

4. Detailed Root Cause Analysis

Step 1: Operator activates implementation without finalizing initialization

Tx 0xe5b892256096f0771e69e60c55ed29170839dcaba95c37f85de10d04acf8fd94 upgraded proxy implementation to 0x0e31530e....

Traces:
  [15493] LiteV3BridgeAggregatorProxy::fallback(...)
    ├─ [10610] EmptyImplementationUUPS::upgradeTo(0x0E31530E...)
    │   ├─ emit Upgraded(implementation: 0x0E31530E...)
    │   ├─ storage changes:
    │   │   @ EIP1967 slot: 0x...4fd81c9e... -> 0x...0e31530e...

Step 2: Open authority window is visible on-chain

RPC spot checks around the incident show implementation switched but owner remained zero:

block=24434263 owner=0x0000000000000000000000000000000000000000 impl=0x0000000000000000000000000e31530ea382ea2cde14cbff0f49110865fc7d5e
block=24434264 owner=0x0000000000000000000000000000000000000000 impl=0x0000000000000000000000009a4400ea971dd31c8ea1712642b87723c029688a

Step 3: Adversary executes takeover transaction in the gap

Tx 0x109274c9a495df5083712800bd0771a02906c1f5986d0bd06c904c667bb511d4 was sent from 0xafaa0819f1898585b7547b3fbb89dac72d09946d to Multicall3 0xca11bde05977b3631167028862be2a173976ca11 (selector 0x252dba42). The call tree shows helper 0x04202d7e... invoking proxy selectors including initialize(address) (0xc4d66de8) and upgradeToAndCall(address,bytes) (0x4f1ef286).

{
  "txhash": "0x109274c9a495df5083712800bd0771a02906c1f5986d0bd06c904c667bb511d4",
  "tx_from": "0xafaa0819f1898585b7547b3fbb89dac72d09946d",
  "tx_to": "0xca11bde05977b3631167028862be2a173976ca11",
  "pre_value": "0x...0e31530ea382ea2cde14cbff0f49110865fc7d5e",
  "post_value": "0x...9a4400ea971dd31c8ea1712642b87723c029688a"
}

Step 4: Receipt and slot-diff confirm ownership and implementation takeover

Causal tx receipt logs for the proxy include:

  • OwnershipTransferred topic with new owner 0x04202d7ec47587562e622a2f4f1fbc7fac588473.
  • Upgraded(0x9a4400ea971dd31c8ea1712642b87723c029688a).

State-diff resolution for block 24434264 attributes the EIP-1967 implementation slot mutation directly to this tx, from operator-intended implementation 0x0e31530e... to attacker-selected 0x9a4400ea....

Step 5: Trusted initialize happens later under already-mutated implementation context

Tx 0x61af0d602cc251d7fb6faee8124fc3064536c4683622c9eaf3dd747674038e3a (block 24434305) later calls initialize(0xc149...) and emits OwnershipTransferred(0x0 -> 0xc149...), but implementation remains 0x9a4400ea.... This demonstrates that the authority window had already been exploited and implementation integrity had already been altered.

5. Adversary Flow Analysis

Adversary-related accounts identified from trace and receipt evidence:

  • 0xafaa0819f1898585b7547b3fbb89dac72d09946d (EOA): sender of the causal takeover tx.
  • 0x04202d7ec47587562e622a2f4f1fbc7fac588473 (contract): helper contract that executes the proxy call sequence.

End-to-end ACT flow:

  1. Observe public operator upgrade tx that enables real implementation but leaves owner unset.
  2. Submit attacker-crafted tx to Multicall3, forwarding through helper contract.
  3. Call proxy initialize(address) first, assigning privileged state to adversary-controlled helper.
  4. Call proxy upgrade path (upgradeToAndCall) to move implementation to 0x9a4400ea....
  5. Commit post-state where unauthorized implementation is live before trusted initializer finalization.

Stage mapping with transaction evidence:

  • Setup window: 0xe5b892256096f0771e69e60c55ed29170839dcaba95c37f85de10d04acf8fd94 (block 24434263).
  • Takeover tx: 0x109274c9a495df5083712800bd0771a02906c1f5986d0bd06c904c667bb511d4 (block 24434264).
  • Post-takeover operator interaction: 0x61af0d602cc251d7fb6faee8124fc3064536c4683622c9eaf3dd747674038e3a (block 24434305).

Additional related transactions tracked in the analysis context:

  • Ethereum: 0x94c994929d91adfcacbdd95da5905b67e6ee1e1fee400af0de5e653bdc20f380 (seed).
  • Arbitrum: 0x1667e2d501791ee1b1a456edcacdc7c83da6a91580c3164a3ef7912a7b4b75bf, 0x795b14fd6f8b30eeff97aefdb7d9b80b8cf29bffe16972459e81ff53939bb42b (related context).

6. Impact & Losses

Measured impact is non-monetary but critical: unauthorized control and implementation integrity loss on an upgradeable proxy. The implementation slot changed from intended 0x0e31530e... to unauthorized 0x9a4400ea... during a public initialization race window, enabling persistent backdoored logic risk.

Observed losses in deterministic token terms: none quantified in provided evidence (N/A monetary loss). Security impact is control-plane compromise of a production proxy and invalidation of trusted upgrade sequencing assumptions.

7. References

  • Root cause source artifact: /workspace/session/root_cause.json.
  • Proxy source: /workspace/session/artifacts/collector/iter_1/contract/1/0x3f568ac2bb7a7099a243d11419185c922cfab766/source/forge_clone/src/bridge-aggregator/proxy.sol.
  • Implementation source (initialize, _authorizeUpgrade): /workspace/session/artifacts/auditor/iter_1/source_spot/0x0e31530ea382ea2cde14cbff0f49110865fc7d5e/src/bridge-aggregator/evm/main.sol.
  • Proxy tx timeline: /workspace/session/artifacts/collector/iter_1/address/1/0x3f568ac2bb7a7099a243d11419185c922cfab766/txlist.normal.full.json.
  • Upgrade tx trace: /workspace/session/artifacts/collector/iter_2/tx/1/0xe5b892256096f0771e69e60c55ed29170839dcaba95c37f85de10d04acf8fd94/trace.cast.log.
  • Causal state-diff and call trace: /workspace/session/artifacts/collector/iter_2/state_diff/1/block_24434264/slot_mutation_resolution.json, /workspace/session/artifacts/collector/iter_2/state_diff/1/block_24434264/causal_tx.debug_traceTransaction.call_tracer.json.
  • Causal tx receipt: /workspace/session/artifacts/collector/iter_2/state_diff/1/block_24434264/causal_tx.eth_getTransactionReceipt.json.
  • RPC spot checks (owner + implementation slot): /workspace/session/artifacts/auditor/iter_2/rpc_spot_checks_eth_proxy.txt.
  • Post-takeover initialize trace: /workspace/session/artifacts/collector/iter_2/tx/1/0x61af0d602cc251d7fb6faee8124fc3064536c4683622c9eaf3dd747674038e3a/trace.cast.log.
  • Key tx hashes: 0xe5b892256096f0771e69e60c55ed29170839dcaba95c37f85de10d04acf8fd94, 0x109274c9a495df5083712800bd0771a02906c1f5986d0bd06c904c667bb511d4, 0x61af0d602cc251d7fb6faee8124fc3064536c4683622c9eaf3dd747674038e3a.