Reproduced Exploit

TrustSwap LockToken: withdrawTokens transfers before clearing nftMinted

Aug 2022•Other•reentrancy•2 min read

Chain

Other

Category

reentrancy

Date

Aug 2022

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: 57521-reentrancy-in-main-functions-zokyo-none-trustswap-markdown. Standalone Foundry PoC and full write-up: 57521-reentrancy-in-main-functions-zokyo-none-trustswap-markdown_exp in the evm-hack-registry mirror.


Vulnerability classes: vuln/reentrancy/single-function

Reproduction: faithful local reproduction of AuditVault 57521. withdrawTokens calls token.transfer and nft.burn before nftMinted[id] = false. A token callback re-enters the same id and pulls the deposit a second time.

Root cause#

Zokyo's TrustSwap review of LockToken.withdrawTokens reports external transfer and burn calls before nftMinted[id] is written false. While the flag is still true, the same id can be withdrawn again.

SOLIDITY
uint256 amount = depositOf[id];
// @> VULN: external transfer and burn run before nftMinted[id] is cleared
token.transfer(msg.sender, amount);
nft.burn(id);
nftMinted[id] = false;

Attack path#

flowchart TD A["Attacker and victim each lock 1000 LOCK"] --> B["Pool holds 2000 LOCK and both ids are minted"] B --> C["Attacker calls withdrawTokens on their id"] C --> D["token.transfer runs while nftMinted is still true"] D --> E["Callback reenters withdrawTokens for the same id"] E --> F["Second transfer pulls the victim lock"] F --> G["Sink receives the stolen 1000 LOCK"]

Impact#

The attacker deposited 1,000 LOCK and finished with that principal back. The other locker's 1,000 LOCK was transferred as well and forwarded to the sink. A fixed withdrawTokens clears nftMinted before the external calls, the reentered withdraw reverts, and the victim lock stays intact.

PoC#

BASH
cd 57521-reentrancy-in-main-functions-zokyo-none-trustswap-markdown_exp
forge test -vv

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.