Closed code423n4 closed 2 years ago
addressed in other comments
Using transferFrom instead of safeTransferFrom in order to avoid DoS or reentrancy concerns (and save gas) is a common approach. The concern raised is potentially valid however. Grouping with the warden's QA report #145
@HardlyDifficult the readme doesn't mention that it's in order to save gas/avoid reentrancy, which usually is required for things like this to be downgraded
For some protocols I would support a higher severity for this issue. But an NFT marketplace is different IMO. The user is attempting to purchase an NFT - if they send it to an unsupported wallet that was an explicit decision they made as the intent was clearly to acquire that asset.
as I mention above, it's not always the buyer that necessarily is at risk - the tipper does not know whether the address being used can handle it, and it's not necessarily the maker's address
Lines of code
https://github.com/code-423n4/2022-05-opensea-seaport/blob/4140473b1f85d0df602548ad260b1739ddd734a5/contracts/lib/TokenTransferrer.sol#L239-L253
Vulnerability details
Impact
By not using
safeTransferFrom()
on NFTs it's possible for the NFT being bought or tipped, to be sent to a contract that is unable to handle it and therefore the NFT may be locked forever.This has been found to be of Medium risk in multiple contests and falls into the sponsor's description of the Medium tier: might lead to a small subset of funds being at risk
Note that this report is only about the optimized version of the code. I've split off a similar issue in the reference code to my QA report, since issues with the reference code are considered to be of low risk
Proof of Concept
The EIP-721 standard says the following about
transferFrom()
:https://github.com/ethereum/EIPs/blob/78e2c297611f5e92b6a5112819ab71f74041ff25/EIPS/eip-721.md?plain=1#L103-L113
The sponsors code has no such capability checks, and uses
transferFrom()
rather thansafeTransferFrom()
in the optimized code:https://github.com/code-423n4/2022-05-opensea-seaport/blob/4140473b1f85d0df602548ad260b1739ddd734a5/contracts/lib/TokenTransferrer.sol#L239-L253
https://github.com/code-423n4/2022-05-opensea-seaport/blob/4140473b1f85d0df602548ad260b1739ddd734a5/contracts/lib/TokenTransferrerConstants.sol#L85
https://github.com/code-423n4/2022-05-opensea-seaport/blob/4140473b1f85d0df602548ad260b1739ddd734a5/contracts/lib/TokenTransferrerConstants.sol#L48-L51
As with one of the previous contests linked to above, there's a mixture of both safe and non-safe functions in the code, because the safe method is used for ERC1155 tokens:
https://github.com/code-423n4/2022-05-opensea-seaport/blob/4140473b1f85d0df602548ad260b1739ddd734a5/contracts/lib/TokenTransferrer.sol#L365-L387
https://github.com/code-423n4/2022-05-opensea-seaport/blob/4140473b1f85d0df602548ad260b1739ddd734a5/contracts/lib/TokenTransferrerConstants.sol#L58-L63
Surprisingly, the Trail of Bits audit flagged the NFT-related code as having a Medium-severity issue due to
onERC721Received()
allowing reentrant changes to the NFT on transfer, even though neither of the versions of the code that was audited actually calls the safe version of the function, which is the only way theonERC721Received()
call would be triggered.As one would expect based on the EIP, neither OpenZeppelin nor solmate execute the
onERC721Received()
callback for the non-safe versionsTools Used
Code inspection
Recommended Mitigation Steps
Use
safeTransferFrom()
rather thantransferFrom()