关键词: NFT
作者: ChainSecLabs
攻击发生时间: 2022年3月3日
损失金额:140万美元
背景介绍
Treasure 通过一种开放和可组合的方法,为日益增长的元宇宙架起了桥梁,使NFTs、DeFi和游戏融合在一起。北京时间2022年3月3日,TreasureDAO遭受黑客攻击,过百枚NFT被盗走,价值约140万美元。
攻击地址及交易信息
攻击者钱包:0x9b1acd4336ebf7656f49224d14a892566fd48e68
攻击者交:0xb169e20b45c6a5b7e5726c812af73c0b48996a4db04b076d6ef484ca5a300d36
出现漏洞的合约地址:
TreasureMarketplaceBuyer:****0x812cda2181ed7c45a35a691e0c85e231d218e273
TreasureMarketplace :****0x2e3b85f85628301a0bce300dee3a6b04195a15ee
攻击过程
TreasureMarketplaceBuyer:0x812cdA2181ed7c45a35a691E0C85E231D218E273
攻击者的交易过程
攻击者调用TreasureMarketplaceBuyer合约中buyItem方法获取了NFT,传入的_quantity为0,但是仍然得到了一枚NFT,是什么原因呢?
漏洞分析
我们分析一下TreasureMarketplaceBuyer合约中butItem函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| function buyItem( address _nftAddress, uint256 _tokenId, address _owner, uint256 _quantity, uint256 _pricePerItem ) external { (, uint256 pricePerItem,) = marketplace.listings(_nftAddress, _tokenId, _owner);
require(pricePerItem == _pricePerItem, "pricePerItem changed!");
uint256 totalPrice = _pricePerItem * _quantity;//0 IERC20(marketplace.paymentToken()).safeTransferFrom(msg.sender, address(this), totalPrice); IERC20(marketplace.paymentToken()).safeApprove(address(marketplace), totalPrice);
marketplace.buyItem(_nftAddress, _tokenId, _owner, _quantity);
if (IERC165(_nftAddress).supportsInterface(INTERFACE_ID_ERC721)) { IERC721(_nftAddress).safeTransferFrom(address(this), msg.sender, _tokenId); } else { IERC1155(_nftAddress).safeTransferFrom(address(this), msg.sender, _tokenId, _quantity, bytes("")); } }
|
分析buyItem可以发现,当_quantity为0时我们可以使得totalPrice为0,从而完成0资金购买,然后进入marketplace中的buyItem
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| function buyItem( address _nftAddress, uint256 _tokenId, address _owner, uint256 _quantity ) external nonReentrant isListed(_nftAddress, _tokenId, _owner) validListing(_nftAddress, _tokenId, _owner) { require(_msgSender() != _owner, "Cannot buy your own item");
Listing memory listedItem = listings[_nftAddress][_tokenId][_owner]; require(listedItem.quantity >= _quantity, "not enough quantity");
// Transfer NFT to buyer if (IERC165(_nftAddress).supportsInterface(INTERFACE_ID_ERC721)) { IERC721(_nftAddress).safeTransferFrom(_owner, _msgSender(), _tokenId); } else { IERC1155(_nftAddress).safeTransferFrom(_owner, _msgSender(), _tokenId, _quantity, bytes("")); } //other code }
|
在marketplace中我们发现如果我们买的是ERC721nft那么_quantity是0也不会影响我们得到一个NFT。因此我们0资金来换取了一个有价值的NFT
安全建议
- 有关代币转移的操作都应慎重考虑
- 应检查用户购买数量的合法性,同时防止零资金购买NFT。
- 将ERC721及ERC1155协议的NFT进行分开处理,避免混淆情况发生
参考链接
https://blog.csdn.net/SierraW/article/details/123275174
https://zhuanlan.zhihu.com/p/475681450