白帽黑客|白帽黑客 Samczsun:针对 NFT 资产的攻击会越来越频繁( 二 )


事实证明,在这种情况下,这些函数更多的是后者,而较少会是前者。这是特别令人遗憾的,因为在 transfer 和 safeTransfer 函数之间进行选择时,你为什么不选择安全的那个函数呢?名字都体现出来了!
好吧,其中的一个原因可能是我们的老朋友 reentrancy (可重入性),或者我一直在努力将其重命名为:不安全的外部调用。回想一下,如果接收方是攻击者控制的,则任何外部调用都可能不安全,因为攻击者可能会导致你的合约转换为未定义状态。根据设计,这些「安全」函数执行对代币接收者的外部调用,通常在铸造或转移期间由发送者控制。换句话说,这实际上是不安全外部调用的教科书示例。
但是,你可能会问自己,如果允许接收方合约拒绝他们无法处理的转账,那最坏的后果是什么?好吧,让我通过两个案例研究来回答这个问题。
例子 1: HashmasksHashmasks 是一个供应有限的 NFT 头像项目,用户每次交易最多可以购买 20 个 mask NFT (尽管它们已经售罄数月了)。下面是购买 mask 的函数:
白帽黑客|白帽黑客 Samczsun:针对 NFT 资产的攻击会越来越频繁
文章插图
你可能觉得这个函数看起来非常合理。然而,正如你可能已经预料到的,在 _safeMint 调用中隐藏着一些险恶的东西。 让我们来看看。
为了安全性,这个函数对 token 的接受者执行了一次 callback 回调,以检查他们是否愿意接受转账。然而,我们是 token 的接收者,这意味着我们刚刚得到了一次 callback 回调,在这个点上我们可以做任何我们想做的事情,包括再次调用 mintNFT 函数。如果我们这样做,我们将在仅铸造了一个 mask 后重调用该函数,这意味着我们可以请求再铸造另外 19 个 mask。这导致最终铸造出了 39 个 mask NFT,尽管规则允许铸造的最大数量只有 20 个。
例子 2: ENS 域名封装器最近,来自 ENS 的 Nick Johnson 联系了我,他想让我看看他们正在进行的 ENS 域名封装器工作。这个域名封装器允许用户用新的 ERC-1155 token 代币化他们的 ENS 域名,这提供了对细粒度权限以及更一致的 API 的支持。
概括地说,为了封装任何 ENS 域名(更具体地说,除了 2LD.eth 之外所有的 ENS 域名),你必须首先批准域名封装器以访问你的 ENS 域名。然后,你调用 wrap(bytes,address,uint96,address),它既为你铸造一个 ERC-1155 token,也负责管理底层的 ENS 域名。
下面就是这个 wrap 函数,它相当简单。首先,我们调用 _wrap,它执行一些逻辑并返回哈希域名。然后,我们确保交易发送方确实是 ENS 域名的所有者,然后再接管该域名。请注意,如果发送方不拥有底层的 ENS 域名,则整个交易应还原,撤销在 _wrap 中所做的任何更改。
白帽黑客|白帽黑客 Samczsun:针对 NFT 资产的攻击会越来越频繁
文章插图
下面是 _wrap 函数本身,这里没有什么特别的。
白帽黑客|白帽黑客 Samczsun:针对 NFT 资产的攻击会越来越频繁
文章插图
不幸的是,正是这个 _mint 函数,它可能会给毫无戒心的开发者带来可怕的惊喜。ERC-1155 规范规定,在铸造 token 时,应咨询接收者是否愿意接受该 token。在深入研究库代码(该代码库根据 OpenZeppelin 的基础稍作了修改)后,我们发现情况确实如此。
白帽黑客|白帽黑客 Samczsun:针对 NFT 资产的攻击会越来越频繁
文章插图
但这到底对我们有什么好处呢?好的,我们再一次看到了一个不安全的外部调用,我们可以用它来执行重入攻击。具体地说,请注意,在 callback 回调期间,我们拥有了代币 ENS 域名的 ERC-1155 token,但域名封装器尚未验证我们拥有基础 ENS 域名本身。这使我们能够在不实际拥有 ENS 域名的情况下对其进行操作。例如,我们可以要求域名封装器解开我们的域名,燃烧掉我们刚刚铸造的 token 并获取底层的 ENS 域名。

推荐阅读