Under normal behavior, once a buyer purchases an NFT, the proceeds from that completed sale should remain claimable only by the seller who actually sold the NFT. The payout recipient for that sale should be immutable after the sale is completed.
In the current implementation, sale settlement is derived from s_listings[_listingId], but that storage slot is keyed by tokenId and is fully overwritten every time the token is listed again. After purchasing the NFT, the buyer becomes the owner and can immediately call list(tokenId, newPrice), which rewrites s_listings[tokenId].seller to the buyer. The buyer can then make the new listing inactive and call collectUsdcFromSelling(tokenId). Since collectUsdcFromSelling() uses the current s_listings[tokenId].seller as the authorization source, the buyer can claim the proceeds that belong to the previous seller.
Likelihood:
The bug occurs whenever a buyer purchases a token and relists that same token before the previous seller collects proceeds.
The exploit path is straightforward because listing ownership naturally transfers to the buyer after buy(), and list() overwrites the prior seller in the same storage slot.
Impact:
The original seller can lose the proceeds from a completed sale.
The buyer can redirect the previous seller’s payout to themselves.
The contract’s pooled USDC becomes misallocated, breaking core sale-settlement guarantees.
Paste this inside NFTDealersTest.t.sol:
Separate active listing state from completed sale settlement state. A completed sale should create immutable payout data that cannot be overwritten by later listings of the same token.
One possible direction:
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.