Reproduced Exploit
Securitize OnRamp replay — signed transactions remain valid after use
executePreApprovedTransaction includes a nonce in the signed payload, but only increments the stored nonce. It never requires the supplied nonce to equal the current expected nonce. A once-valid nonce-zero subscription can therefore be submitted twice, executing the destination call twice and issui…
Chain
Other
Category
bridge
Date
Jul 2025
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: 64270-missing-nonce-validation-in-signature-verification-allows-tr. Standalone Foundry PoC and full write-up: 64270-missing-nonce-validation-in-signature-verification-allows-tr_exp in the
evm-hack-registrymirror.
Vulnerability classes: vuln/bridge/replay · vuln/auth/signature-validation · vuln/logic/missing-validation
Reproduction: Local synthetic, runnable offline with
forge test -vvvin the PoC folder. The browser replay callsExploit.run()and verifies duplicate DS issuance.
Key info#
| Field | Value |
|---|---|
| Loss | 200 synthetic DS tokens issued after replaying one 100-USDC authorization |
| Vulnerable contract | SecuritizeOnRamp |
| Attacker | Any party able to resubmit a previously valid pre-approved transaction |
| Chain | Local EVM synthetic (audit finding; no historical fork) |
| Compiler | Solidity 0.8.24 |
| Bug class | Missing nonce validation / signature replay |
TL;DR#
executePreApprovedTransaction includes a nonce in the signed payload, but only increments the stored nonce. It never requires the supplied nonce to equal the current expected nonce. A once-valid nonce-zero subscription can therefore be submitted twice, executing the destination call twice and issuing duplicate investor tokens.
The vulnerable code#
The reduction preserves the material line from SecuritizeOnRamp.sol:
// FIX: require(txData.nonce == noncePerInvestor[txData.senderInvestor], "invalid nonce");
noncePerInvestor[txData.senderInvestor] = noncePerInvestor[txData.senderInvestor] + 1; // @> VULN: stored nonce is incremented but txData.nonce is never validated, so an old valid signature replays.
(bool ok,) = txData.destination.call(txData.data);
Root cause#
Authenticity and freshness are separate properties. Signature recovery proves an authorization was produced by a permitted signer; it does not prove that the authorization has not already been consumed. Incrementing noncePerInvestor after recovery does not invalidate an old payload unless txData.nonce is compared to that stored value first.
Preconditions#
- A valid signed subscription payload exists.
- The investor still has the assets/allowance required by the destination on replay.
- The attacker can submit the valid payload again.
Attack walkthrough#
Exploit.run()funds its synthetic investor with 200 USDC and prepares one nonce-zero subscription.- The first call transfers 100 USDC and issues 100 DS tokens.
- The on-ramp increments its stored nonce but accepts no equality check.
- The exact same signature and
txDataexecute again. - The invariant assertions show stored nonce
2, 200 DS issued, and all 200 USDC consumed.
Diagrams#
Impact#
Every previously valid authorization can be replayed until the investor's available balance or allowance is exhausted. That can duplicate subscriptions, swaps, token issuance, and accounting effects.
Remediation#
Before signature recovery and the destination call, require txData.nonce == noncePerInvestor[txData.senderInvestor]; then increment it exactly once. Reject mismatched or stale nonces.
How to reproduce#
cd /workspaces/RustroverProjects/audits/evm-hack-registry/64270-missing-nonce-validation-in-signature-verification-allows-tr_exp
forge test -vvv
The browser bundle is generated from scripts/poc-configs/64270-missing-nonce-validation-in-signature-verification-allows-tr.mjs.
Sources#
Sources & further analysis#
Reproductions & code
- Standalone PoC + full trace: 64270-missing-nonce-validation-in-signature-verification-allows-tr_exp (evm-hack-registry mirror).
- AuditVault finding: 64270-missing-nonce-validation-in-signature-verification-allows-tr.
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.