Position Exchange 重入漏洞解析

Amber Group 区块链安全团队和独立研究员Rivaill再次发现了一个重入漏洞,并向漏洞悬赏平台ImmuneFi提交漏洞报告。由于项目方(Position Exchange)在修复漏洞后就与我们失去联系,我们决定在此撰文中公布详情。

0x00 Position Exchange

去中心化加密平台Position Exchange允许用户使用POSI 代币铸造NFT,并质押NFT 以赚取更多的POSI。如HOWTO文档中所述,用户可以铸造的NFT 角色共有六种,借此来获得随机的挖矿效率增益。

举例来说,用户支付50 POSI 铸造下方这个Pilot NFT,随机生成的挖矿效率为154.06 %,因此他/她获得50 x 154.06% = 76.28 POSI 的算力。如果用户选择质押该算力,会因此获得额外的NFT 矿池奖励分成。

为了实现上述的NFT 质押机制,后端的NFTReward 智能合约实现了stake() 和unstake() 函数,让用户转入/转出基于ERC-721 的NFT。然而,由于ERC-721 标准实现中内建的回调机制,NFTReward 的运作逻辑产生了问题,导致攻击者能够多次重入该智能合约而获利。

0x01 漏洞

前面提的unstake () 函数,他的基本功能是将某个ERC721 代币取回给调用者。但是如下图所示,我们注意到第283 行中使用的safeTransferFrom() 函数被嵌入了一个特殊的回调机制,攻击者可利用此漏洞拦截unstake() 调用并插入恶意的程式码。此外,第283 行之后有两行改变状态的语句(第285-286 行),这违反了智能合约基本的Checks-Effects-Interactions 原则。

具体来说,在OpenZeppelin 的ERC721 实现中,在传输ERC721 代币时,若接收地址是合约时会调用onERC721Received() 外部函数(第395 行),这是此漏洞能被利用的关键。

透过这种方式,攻击者可以藉由任何的外部函数进入NFTReward 合约窜改相关数据。如何做到?我们注意到下面两行是unstake() 函数调用safeTransferFrom() 之后唯二更新过状态的两个地方:

如下方的程式码片段所示,这些状态是在stake() 函数中被设置的。

我们认为这个的设计的初衷是先暂存gegoId 对应NFT 的余额(_stakeBalances[geogoId])和权重(_stakeWeightes[geogoId])。随后,如果用户选择unstake() 该NFT,暂存的值将用于更新与该用户有关的全域变数_weightBalances 和_degoBalances(下面的第276 和280 行)。

因此,如果暂存的余额和权重在用于更新全域状态之前被窜改,意味着_weightBalances 和_degoBalances 可能也会被篡改。此外,我们注意到_weightBalances 值会被用于计算奖励,这意味着可以利用被篡改的状态来获取额外的奖励,甚至可能耗尽奖励池。

0x02 漏洞利用

下图展示了篡改_weightBalances 的六个步骤:

  1. 攻击者创建一个恶意合约Exp,并从Exp 呼叫stake() 函数。如上图所示,为了成功做到后续的劫持操作,此恶意合约Exp 实现了一个外部函数onERC721Received();
  2. 攻击者从Exp 合约触发了unstake() 函数;
  3. 透过safeTransferFrom(),Exp 合约成功劫持了unstake() 函数的调用;
  4. 从Exp 合约重新进入stake() 函数,使其之后能够调用第二次unstake() 函数;
  5. 当Exp 合约返回到被劫持的unstake() 调用时,第四步骤设置的_stakeWeightes 会被重置设为0;
  6. 攻击者发出第二个unstake() 调用并使用归零的_stakeWeightes 篡改_weightBalances 变数。

透过上述步骤,攻击者可以只用一个NFT 持续累积_weightBalances,并且可以在没有质押任何NFT 的情况下,透过NFTReward 合约中的harvest() 函数不断获取奖励。

下方恶意合约的程式码说明了我们如何模拟攻击。在trigger() 函数中,我们执行了多次[stake(), unstake(), unstake()] 调用。每当unstake() 将NFT 转移回恶意合约时,onERC721Received() 函数就会劫持unstake() 并在flag 为true 时发出另一个stake() 调用,这就是重入攻击窜改状态的流程。

下方的eth-brownie 截图证明了我们的猜想。具体来说,我们:

  1. 通过eth-brownie 的模拟,「借用」了NFT #1183410;
  2. 在NFTReward 合约上窜改状态;
  3. 「归还」该NFT;
  4. 收获奖励。

最后,我们的黑客在没有质押任何NFT 的情况下以4.93 POSI 获利出场,这也间接证明了攻击者可以创建多个合约,为每个合约伪造状态,并透过来回转移同一个NFT 来获得奖励。

0x03 时间轴和缓解措施

该漏洞于3 月9 日通过ImmuneFi 提交;3 月21 日,Position Exchange 团队确认了该问题,并将其评为高危漏洞; 3 月22 日,我们观察到一个新的NFTReward 合约被部署上链,此合约加入了reentrancy guard。但自那以后,我方仍未能收到Position Exchange 团队的任何回覆。

完整的时间轴如下图所示:

2022/03/09 提交

2022/03/21 确认

2022/03/22 修复

https://bscscan.com/tx/0x67875ba082a5a5f7e570ce1f4035dadf0daa0b3cb22e527ac89ef37ce38a4257

除了Position Exchange,我们还联系了Dego Finance和Smarty Pay。两方都部署了类似的NFTReward 合约,且其中含有类似的漏洞。Dego Finance 团队的处理方式是透过将DEGO 代币转移到新代币。与此同时,Smarty Pay 团队升级了他们的NFTReward 合约,并将所有的用户状态迁移到了新版本。据我们了解,这三个具有漏洞的项目,在我们披露细节之前皆没有被真的攻击。

本文链接地址:https://www.wwsww.cn/jishu/12171.html
郑重声明:本文版权归原作者所有,转载文章仅为传播更多信息之目的,如作者信息标记有误,请第一时间联系我们修改或删除,多谢。