Reproduced Exploit

Sweep n Flip — LayerZeroAdapter.executeMessages irrecoverable state

1. Failed LZ deliveries are stored in s_pendingMessagesToExecute. 2. executeMessages runs pending[0] but pop() removes the last element. 3. With [msg1, msg2]: first call delivers token 1 and deletes msg2. 4. Second call re-runs msg1 (already delivered) and clears the queue — token 2 is gone forever.

Nov 2024Otherdos3 min read

Chain

Other

Category

dos

Date

Nov 2024

Source

AuditVault

EVM Playground

Source-level debugger — step opcodes and Solidity in sync

evm-hack-analyzer

The attack is replayed in an in-browser EVM preloaded with the exact dumped fork state. The execution tree shows every call; step by Solidity line or by opcode across all depths — source, Stack, Memory, Storage, Balances (native / ERC-20 / NFT), Transient storage and Return value stay in sync. Click a tree node, opcode, or source line to jump. No backend, no live RPC.

Loading fork state…

Source & credit. Reproduction of a public audit finding curated by AuditVault — the original finding: 46494-layerzeroadapterexecutemessages-could-lead-to-irrecoverable. Standalone Foundry PoC and full write-up: 46494-layerzeroadapterexecutemessages-could-lead-to-irrecoverable_exp in the evm-hack-registry mirror.


Vulnerability classes: vuln/dos/frozen-funds · locked-funds · cross-chain-message · bridge-message-validation

Reproduction: self-contained Foundry PoC, offline, forge-std only. Full trace: output.txt. PoC: test/46494-layerzeroadapterexecutemessages-could-lead-to-irrecoverable_exp.sol.


Key info#

ImpactHIGH — pending cross-chain NFT delivery permanently lost
ProtocolSweep n Flip Bridge / LayerZeroAdapter
Vulnerable codeLayerZeroAdapter.executeMessages — executes index 0, pops last
Bug classArray pop mismatch → wrong message deleted, correct one stuck/re-run
FindingCantina — Sweep n Flip Bridge, Nov 2024 · #46494 · reporter slowfi
Reportcantina_sweepnflip_bridge_november2024.pdf
SourceAuditVault
StatusAudit finding — PRs 9/13; residual issues noted by Cantina
Compiler^0.8.24 (PoC)

TL;DR#

  1. Failed LZ deliveries are stored in s_pendingMessagesToExecute.
  2. executeMessages runs pending[0] but pop() removes the last element.
  3. With [msg1, msg2]: first call delivers token 1 and deletes msg2.
  4. Second call re-runs msg1 (already delivered) and clears the queue — token 2 is gone forever.

The vulnerable code#

SOLIDITY
bytes memory payload = s_pendingMessagesToExecute[0];
(bool ok,) = address(bridge).call(payload);
// ...
s_pendingMessagesToExecute.pop(); // @> VULN: pops LAST, not the executed index-0 message

Fix: execute from the last index (swap-and-pop), or assign nonces per message.


Root cause#

Mismatch between which message is executed and which is removed from storage.

Preconditions#

  • At least two messages queued after failed delivery.
  • Operator retries with limitToExecute_ = 1 (or any partial limit).

Attack walkthrough#

  1. Queue failed deliveries for token 1 and token 2.
  2. Re-enable deliveries; executeMessages(1) → token 1 delivered, msg2 popped.
  3. executeMessages(1) → re-executes msg1 (reverts ALREADY_DELIVERED), pops msg1.
  4. HARM: token 2 never delivered, no longer pending — NFT permanently locked.

Diagrams#

sequenceDiagram participant Op as Operator participant LZ as LayerZeroAdapter participant B as Bridge Note over LZ: pending = msg1, msg2 Op->>LZ: executeMessages(1) LZ->>B: call pending 0 = msg1 B-->>LZ: ok token 1 delivered Note over LZ: pop last → deletes msg2 Note over LZ: pending = msg1 Op->>LZ: executeMessages(1) LZ->>B: call pending 0 = msg1 again B-->>LZ: revert ALREADY_DELIVERED Note over LZ: pop → queue empty, msg2 lost

Impact#

Permanent lock of bridged NFTs whose delivery messages are silently dropped from the retry queue.

Sources#


Sources & further analysis#

Reproductions & code

Alerts & third-party analyses

  • Web3Sec X hacked database: search.
  • Rekt leaderboard: search.
  • Solodit incident search: search.

These dashboards index community alerts tweets, post-mortems, and independent write-ups. Reach them through the protocol name above to cross-check this reproduction against other analyses.