Reproduced Exploit
NFTMirror — lzReceive for releaseOnEid results in OOG
1. releaseOnEid builds LZ options via getSendOptions(tokenIds). 2. Budget = 80_000 + 20_000 * length — too low for destination mint/transfer (and worse with transfer validators). 3. DVN lzReceive with that stipend OOGs; payload stored for retry. 4. Every release path needs manual higher-gas retry.
Chain
Other
Category
untagged
Date
Dec 2024
Source
AuditVault
EVM Playground
Source-level debugger — step opcodes and Solidity in sync
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.
Source & credit. Reproduction of a public audit finding curated by AuditVault — the original finding: 50038-h-02-lzreceive-call-for-releaseoneid-results-in-oog-error-pa. Standalone Foundry PoC and full write-up: 50038-h-02-lzreceive-call-for-releaseoneid-results-in-oog-error-pa_exp in the
evm-hack-registrymirror.
Vulnerability classes: dos-resistance · unbounded-loop · known-pattern
Reproduction: self-contained Foundry PoC with only
forge-std— no fork. Full trace: output.txt. PoC: test/50038-h-02-lzreceive-call-for-releaseoneid-results-in-oog-error-pa_exp.sol.
Key info#
| Impact | HIGH — destination lzReceive always OOGs under protocol gas options; messages stuck for costly retry |
| Protocol | NFTMirror — NFTShadow.getSendOptions / LayerZero path |
| Vulnerable code | getSendOptions: 80_000 + 20_000 * n underestimates mint (~46.7k+) per token |
| Bug class | Underfunded cross-chain execution gas / DoS |
| Finding | Pashov Audit Group · NFTMirror-security-review 2024-12-30 · H-02 |
| Report | pashov/audits NFTMirror |
| Source | AuditVault |
| Status | Audit finding. Sample+extrapolate gas measurement in synthetic. |
| Compiler | ^0.8.24 (PoC) |
TL;DR#
releaseOnEidbuilds LZ options viagetSendOptions(tokenIds).- Budget =
80_000 + 20_000 * length— too low for destination mint/transfer (and worse with transfer validators). - DVN
lzReceivewith that stipend OOGs; payload stored for retry. - Every release path needs manual higher-gas retry.
The vulnerable code#
uint128 totalGasRequired =
_BASE_OWNERSHIP_UPDATE_COST + (_INCREMENTAL_OWNERSHIP_UPDATE_COST * uint128(tokenIds.length));
// @> VULN: 20k/token << real mint/transfer cost (~46.7k+)
Fix: raise constants; allow user override of lzReceive gas.
Root cause#
Hard-coded gas options ignore real destination work (mint, transfer, optional validateTransfer).
Preconditions#
- User releases shadow NFT(s) to another eid via
releaseOnEid. - Destination handler performs mint/transfer under the computed stipend.
Attack walkthrough / PoC#
- Compute budget for 1 token →
100_000. - Measure full-gas unlock cost for 1 token → exceeds budget.
- Call
lzReceivewith budget → fails / OOG;payloadHashes != 0(retry queue).
Diagrams#
Impact#
Cross-chain release is non-functional under default options; every message needs a higher-gas retry (cost + latency DoS).
Taxonomy#
genome: dos-resistance,unbounded-loop,known-patternseverity/high·sector/bridge·sector/nft
Sources#
Sources & further analysis#
Reproductions & code
- Standalone PoC + full trace: 50038-h-02-lzreceive-call-for-releaseoneid-results-in-oog-error-pa_exp (evm-hack-registry mirror).
- AuditVault finding: 50038-h-02-lzreceive-call-for-releaseoneid-results-in-oog-error-pa.
Alerts & third-party analyses
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.