关键词: NFT

作者: ChainSecLabs

攻击发生时间: 2022年3月3日

损失金额:140万美元

背景介绍

Treasure 通过一种开放和可组合的方法,为日益增长的元宇宙架起了桥梁,使NFTs、DeFi和游戏融合在一起。北京时间2022年3月3日,TreasureDAO遭受黑客攻击,过百枚NFT被盗走,价值约140万美元。

攻击地址及交易信息

攻击者钱包:0x9b1acd4336ebf7656f49224d14a892566fd48e68

攻击者交:0xb169e20b45c6a5b7e5726c812af73c0b48996a4db04b076d6ef484ca5a300d36

出现漏洞的合约地址:

TreasureMarketplaceBuyer:****0x812cda2181ed7c45a35a691e0c85e231d218e273

TreasureMarketplace :****0x2e3b85f85628301a0bce300dee3a6b04195a15ee

攻击过程

TreasureMarketplaceBuyer:0x812cdA2181ed7c45a35a691E0C85E231D218E273

攻击者的交易过程

img

攻击者调用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