articleDec 25, 2022
Damn Vulnerable DeFi — Free Rider (msg.value reuse in NFT marketplace)
High-level look at DVDeFi Challenge #10 Free Rider: batch NFT purchase logic that reuses a single msg.value across many buys, plus marketplace payout quirks. Solve steps omitted.
Damn Vulnerable DeFi Challenge #10 — Free Rider
Educational wargame by @tinchoabbate. Public contracts live under the free-rider challenge tree. Solve scripts and PoC omitted.
Theme
A marketplace lists six Damn Valuable NFTs at a fixed ETH price. A buyer offers a bounty for delivering all tokens. The player starts with little ETH but has access to a Uniswap V2 pair that can provide short-lived liquidity (flash-swap style capital).
Root-cause concept
msg.valuereuse in a batch buy: an externalbuyMany-style entrypoint loops private_buyOnehelpers. Each iteration still sees the same call’smsg.value, so a payment meant for one NFT can incorrectly satisfy many price checks- Marketplace accounting that pays sellers from contract balance without tightly binding “one payment ↔ one transfer” makes underpayment profitable
- Resale / offer flows on the same marketplace can further distort who holds ETH versus who holds NFTs after a bad buy
Impact (abstract)
In the lab, a player can obtain all listed NFTs and claim the buyer bounty while only providing a fraction of the naive sticker price—then settle any temporary borrowed liquidity inside one transaction. In production terms: batch checkout that trusts msg.value once per multi-item loop undercharges buyers and drains marketplace inventory/funds.
Mitigations / lessons
- For multi-item purchases, require
msg.valueto cover the sum of prices (or pull ERC-20 with explicit per-item amounts)—never re-check the samemsg.valueas if it were fresh each iteration - Prefer pull-payment patterns and explicit accounting events per tokenId
- Separate “buyer bounty escrow” from marketplace treasury logic with clear ownership and reentrancy guards already present where needed
- Treat flash liquidity as a reminder that economic bugs are often one-transaction complete
Solve steps and PoC omitted.