xyyme.eth

发布于 2022-03-25到 Mirror 阅读

Treasure DAO 攻击事件分析

前段时间,Treasure DAO 被攻击,造成市场上一部分的 NFT 可以被以 0 的价格买到。究其原因,其实是由于合约代码中一个很简单的 bug 造成的,这篇文章我们就来看看。

TreasureDAO 是一个 NFT 市场,其相关合约主要有两个(对应的地址均为当时攻击发生时的地址):

代码分析

先来看看 2 合约都有些什么方法:

  • createListing(NFT 上架)
  • updateListing(更新 NFT 相关商品信息)
  • cancelListing(下架)
  • buyItem(购买商品)
  • 一些 set 函数设置一些参数

再来看看主要的 buyItem 方法:

可以看到,购买 ERC1155 和 ERC721 是被放在一个方法中处理的。问题在于对于 1155 来说,tokenId 下的数量是有意义的,对于 721 来说,不存在数量这个字段(不熟悉 1155 的同学可以看看我之前写的这篇文章)。这里放在一起处理,对于 1155 来说没有问题,但是对于 721 来说,如果正常传 1,那么也没有问题,但是如果传大于 1 的值,就可能会造成用户多付几倍的钱,却只买到了一个 NFT。如果传 0,那么就会造成零元购(我猜测项目方在开发时可能就内部约定,在 721 的情况下,前端默认传 1,没有考虑传 0 的情况)。

这里还有一个问题,攻击者是通过调用 1 合约的 buyItem 方法再进入到 2 合约中的,而 2 合约没有对 msg.sender 进行校验。不过,不能确定这是一个漏洞,还是项目方有意为之。由于我没有对项目整体合约架构进行阅读,因此对这里不做评价。

我们可以顺便来看看 1 合约的 buyItem 方法,同样没有对数量参数进行非零校验:

我们再来看看 createListing (NFT 上架)方法:

可以看到,_quantity 参数仅仅对于 ERC1155 起作用,但是在最后的商品信息赋值时,却用到了这个参数。这里如果在上架 ERC721 的时候,数量传 1,没有问题,如果数量传大于 1 的值,就会造成数据紊乱。如果用户在购买的时候也传了大于 1 的值,就可能会多花冤枉钱,这就是我前面说的情况。

总结

造成这次攻击的原因主要在于没有对输入参数作完整的校验,这也是我们在日常开发中经常容易忽视的一点,不能仅仅考虑项目前端调用,还要考虑到有科学家会直接调用合约的情况。

还有就是代码中将 ERC721 和 ERC1155 放在一起处理,会造成一些逻辑混乱,这样就容易出现 bug,这种情况还是要避免。

参考

https://twitter.com/peckshield/status/1499251425393582081?ref_src=twsrc%5Etfw%7Ctwcamp%5Etweetembed%7Ctwterm%5E1499251642171944960%7Ctwgr%5E%7Ctwcon%5Es2_&ref_url=https%3A%2F%2Fcryptopotato.com%2Fhackes-exploit-arbitrum-based-marketplace-treasure-over-100-nfts-stolen%2F

https://www.tuoluo.cn/article/detail-10095262.html

https://www.anquanke.com/post/id/269124

DAO