The Swan NFT marketplace contains a critical vulnerability in its listing and purchase functions where token transfers can occur without proper ownership validation. This could allow an attacker to transfer any NFT that has previously approved the marketplace contract, regardless of ownership. The vulnerability stems from using ownerOf() instead of msg.sender for token transfers.
The vulnerability exists in the listing and purchase functions of the Swan contract:
In the purchase() function:
In the transferRoyalties() function:
The key issue is that these functions do not validate that msg.sender owns the tokens being transferred. Instead, they assume the tokens can be transferred as long as the contract has approval. This violates a core assumption of ERC20/ERC721 token safety:
The contract should only transfer tokens on behalf of the actual current owner
Token approvals should only allow designated operators to initiate transfers
Token transfers should validate ownership at time of transfer
The vulnerability is considered HIGH severity for the following reasons:
Unauthorized Token Transfers
Any user could transfer NFTs that have approved the marketplace contract
No ownership verification is performed at time of transfer
Users who previously approved the contract are at risk of NFT theft
Loss of User Assets
NFTs approved to the contract could be stolen by an attacker
The vulnerability exposes approved NFTs from any collection
Attacker could drain NFTs from multiple victims in a single transaction
Protocol Trust
The vulnerability fundamentally breaks the trust model of the marketplace
Users expect the marketplace to only transfer tokens they explicitly list
Undermines the core security assumptions of the NFT trading protocol
Manual code review
slitherin .
Swan transferRoyalties parameter from is not related to msg.sender token.transferFrom(asset.seller,address(this),buyerFee) (contracts/swan/Swan.sol#265)
Swan purchase parameter from is not related to msg.sender SwanAsset(_asset).transferFrom(listing.seller,address(this),1) (contracts/swan/Swan.sol#294)
Swan purchase parameter from is not related to msg.sender SwanAsset(_asset).transferFrom(address(this),listing.buyer,1) (contracts/swan/Swan.sol#295)
Swan purchase parameter from is not related to msg.sender token.transferFrom(listing.buyer,address(this),listing.price) (contracts/swan/Swan.sol#298)
Reference: https://ventral.digital/posts/2022/8/18/sznsdaos-bountyboard-unauthorized-transferfrom-vulnerability
The following changes are required to fix the vulnerability:
Add approval checks before transfers:
Add ownership validation by requiring msg.sender
to be the token owner:
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.