Under normal behavior, once a sale is executed, the seller’s payout should be calculated from the actual executed sale price. That sale price should become immutable after buy() completes.
In the current implementation, collectUsdcFromSelling() calculates payout from s_listings[_listingId].price, but s_listings is keyed by tokenId and can be overwritten by a later listing of the same NFT. After buying the NFT, the buyer becomes the owner and can re-list the same token, which rewrites the price field for that storage slot. The buyer can then call updatePrice() to set an even higher value, make the new listing inactive, and finally call collectUsdcFromSelling() using the manipulated price instead of the real sale price.
Likelihood:
The bug occurs whenever a buyer purchases a token and re-lists that same token before the original seller collects proceeds.
The exploit path is straightforward because the new owner can overwrite the listing record and then update the price before calling the payout function.
Impact:
Payouts can be calculated from an attacker-controlled value rather than the actual executed sale price.
The contract can overpay the caller and drain pooled USDC.
The original seller’s settlement guarantees are broken because sale accounting is no longer tied to the completed transaction.
Paste this inside NFTDealersTest.t.sol:
Snapshot immutable sale data inside buy() and use that snapshot for settlement. Do not let later listings or price updates affect the payout of an already executed sale.
The contest is live. Earn rewards by submitting a finding.
This is your time to appeal against judgements on your submissions.
Appeals are being carefully reviewed by our judges.
The contest is complete and the rewards are being distributed.