Reproduced Exploit

NetMind: one distribution signature pays the GPU provider twice

Apr 2024•Other•auth•2 min read

Chain

Other

Category

auth

Date

Apr 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: 59205-signature-replay-quantstamp-netmind-markdown. Standalone Foundry PoC and full write-up: 59205-signature-replay-quantstamp-netmind-markdown_exp in the evm-hack-registry mirror.


Vulnerability classes: vuln/auth/signature-replay

Bob deposits 10 NMT. An authorized signer signs one distribution: payment id 1, the GPU provider, 2 NMT GPU fee, 1 NMT platform fee, expiry 2000000000. The digest is keccak256(abi.encode(...)) and is never marked used. The same 65-byte signature, precomputed offline, is submitted a second time. The GPU provider ends with 4 NMT and feeTo with 2 NMT. Only 2 NMT and 1 NMT were authorized. The second use moved another 3 NMT.

SOLIDITY
require(signer == authorizedSigner, "sig");
p.distributed += gpuFee + platFee; // @> VULN: digest is not consumed
token.transfer(gpu, gpuFee);
token.transfer(feeTo, platFee);

Diagrams#

flowchart TD A["Bob deposits 10 NMT"] --> B["Signer signs one distribution of 2 plus 1 NMT"] B --> C["First distribute pays 2 NMT to the GPU provider and 1 NMT to feeTo"] C --> D["The same signature is submitted again"] D --> E["ecrecover still returns the authorized signer"] E --> F["Second use moves another 2 NMT to the GPU provider and 1 NMT to feeTo"] F --> G["GPU provider holds 4 NMT and 4 NMT remains of the deposit"]

Impact#

GPU provider balance is 4 NMT. feeTo balance is 2 NMT. distributed is 6 NMT. The payment contract still holds 4 NMT. The fixed contract stores the digest and the second call reverts, leaving the GPU provider at 2 NMT and feeTo at 1 NMT.

PoC#

BASH
cd 59205-signature-replay-quantstamp-netmind-markdown_exp
forge test -vv

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.