Is safeTransferFrom really safe?
ERC721 NFT contract offers two options for transferring NFTs: transferFrom and safeTransferFrom. You might wonder if safeTransferFrom is safer than transferFrom? The answer is yes and no. Let's break it down:
safeTransferFrom's Safety: This method includes a check to ensure the recipient can correctly handle ERC721 NFTs. This means the NFT is less likely to become "stuck" if the recipient is a contract lacking the necessary NFT handling functionality.The Safety Caveat: While
safeTransferFromprovides a safeguard for the NFT itself, it doesn't guarantee the safety of your contract. In fact, it could introduce a vulnerability to reentrancy attacks. This is because it triggers a function in the recipient contract if the recipient is another contract.
Let's dig into a real-world example. On January 25 2024, an attacker exploited a reentrancy vulnerability on NBLGAME contracts. The estimated total value of the exploited funds is approximately USD $180,000.
Overview
Attacker : https://optimistic.etherscan.io/address/0x1fd0a6a5e232eeba8020a40535ad07013ec4ef12 Attack Contract : https://optimistic.etherscan.io/address/0xe4d41bdd6459198b33cc795ff280cee02d91087b Vulnerable Contract : https://optimistic.etherscan.io/address/0x5499178919c79086fd580d6c5f332a4253244d91 Attack TX: https://optimistic.etherscan.io/tx/0xf4fc3b638f1a377cf22b729199a9aeb27fc62fe2983a65c4d14b99ee5c5b2328
Exploit Analysis
The vulnerability that was exploited stemmed from a reentrancy vulnerability in the withdrawNft function due to its use of safeTransferFrom when returning the NFT.
The attacker first deposited NFT then staked NBL tokens to the contract. While withdrawNFT, the contract sends the NFT back first, then returns the staked token later. Since safeTransferFrom triggers the attacker's contract's onERC721Received function, the attacker had a window of opportunity. The contract's state was inconsistent because the staked NBL amount had not yet been deducted. At this time, the amount of staked NBL is stored in amount so the attacker leveraged onERC721Received to initiate a reentrant call to withdrawNFT, effectively redepositing the NFT back to the contract to call withdrawNFT again.
The second withdrawal successfully retrieved the staked NBL tokens. Subsequently, the first withdrawal also deducted the NBL tokens from the previously stored amount, which was still intact due to the reentrancy. The exploit was success and the attacker received double the staked tokens.
Conclusion
When working with ERC721 NFT smart contracts, always aware that safeTransferFrom function will trigger onERC721Received in the recipient so they might form reentrancy vulnerable. Here are two effective ways to mitigate this risk:
Checks-Effects-Interactions Pattern: This pattern emphasizes updating your contract's state variables before any external calls are made. This prevents an attacker from manipulating the contract's state during the external call.
ReentrancyGuard: Consider using a
ReentrancyGuardmechanism in all public/external functions. This adds a modifier that prevents functions from being re-entered before the initial call is complete.
For maximum security, it's recommended to apply both methods in your contracts whenever possible. While ReentrancyGuard incurs a small gas cost, it provides an additional layer of protection against reentrancy attacks.



