Jarvis Network 闪电贷重入攻击事件分析
根据 NUMEN 链上监控显示,Jan-15-2023 05:43:37 PM +UTC 时间,Jarvis Network 项目遭到攻击,损失 663101 个 MATIC。
具体 tx 如下:
https://polygonscan.com/tx/0x0053490215baf541362fc78be0de98e3147f40223238d5b12512b3e26c0a2c2f

根据调用栈分析,交易中涉及到很多代币转账,所以调用栈很长,我们分析发现在调用过程中存在重入的逻辑。在重入的过程中发现重入前和重入后,对同一个合约的同一个函数调用,传入参数也一样,但是返回值差别很大。
重入前:1002157321772769944
重入后:10091002696492234934

重入发生在 remove_liquidity,这个函数在 (https://polygonscan.com/address/0xfb6fe7802ba9290ef8b00ca16af4bc26eb663a28#code) 这个合约中。remove_liquidity 这个函数在移除流动性的时候会把用户添加的代币返回给用户,由于 polygon 和 evm 是同构链,所以在 matic 转账给合约的时候会进入到合约的重入。详细分析调用栈中重入的部分。

首先看 getUnderlyingPrice(https://polygonscan.com/address/0xcc6aa628516bb46391b05b16e5058c877461cc76#code)上面是逻辑合约,但是不开源。


根据插槽得到 v1=0xe7cea2f6d7b120174bf3a9bc98efaf1ff72c997d,wtoken=0x0d500b1d8e8ef31e21c99d1db9a6444d3adf1270。所以进入 if 分支,然后通过插槽查到_oracles[v1]=0xacf3e1c6f2d6ff12b8aee44413d6834774b3f7a3,继续进入里面的 else 分支后调用 0xacf3e1c6f2d6ff12b8aee44413d6834774b3f7a3 合约的 getUnderlyingPrice 函数。0xacf3e1c6f2d6ff12b8aee44413d6834774b3f7a3 对应的逻辑合约是 0x3803527dcd92ac3e72a0a164db82734daba47fac,这个合约也没有开源。


这个是一些内置的计算,因为攻击涉及到重入,考虑到一定是重入前和重入后的一些变量是转账发生之后计算的,这部分代码不涉及到外部变量,所以问题应该不在这。除此之外还调用了 0x9de。

varg0 是传入的代币地址,也就是 0xe7cea2f6d7b120174bf3a9bc98efaf1ff72c997d。对应的_poolOf[v0]是 0xfb6fe7802ba9290ef8b00ca16af4bc26eb663a28。进入这个合约分析 get_virtual_price 函数。对应代码如下:

self.token 还是 0xe7cea2f6d7b120174bf3a9bc98efaf1ff72c997d,除数是这个代币的总发行量。

看到 D 会影响 get_xcp 的返回。因为重入发生在 remove_liquidity,所以分析 remove_liquidity 函数。详细代码如下:

调用前后 get_virtual_price 返回值变化。


self.D 的更改在转账之后。攻击者在移除流动性的时候,matic 转移到攻击者合约,回调 fallback 时候先查了一下 0xe7cea2f6d7b120174bf3a9bc98efaf1ff72c997d 价格,由于 self.D 更新在转账之后,所以导致之前的价格获取错误。
移除流动性方法流程:1)销毁用户 LP;2)发送给用户质押资金;3)更新 self.D。
self.D 用于价格计算,添加流动性时也对 self.D 更新,并且攻击者此次流动性资金较大。根据 self.D 计算公式 self.D = D - D * amount / total_supply,amount 和 total_supply 近乎相等,正常计算时,self.D 值会小很多。
但由于攻击者在 2)进行了重入,并且比原始价格增高了 10 倍进行借贷,正是因为 self.D 值在添加流动性时增大,而移除流动性时未及时更新。
这里 remove_liquidity 移除流动性方法虽然添加了@nonreentrant('lock') 来防止重入该方法,但由于攻击者重入进入其他合约借贷资金,这里重入锁并不能奏效。
总结:
本次攻击主要是因为变量修改逻辑在外部调用之后,导致价格获取出现异常,并且跨合约重入使得重入锁不能奏效,NUMEN 实验室提醒项目方代码要经过严格的安全审计,变量修改要放在外部调用之前并且价格获取采用多数据源的方式,代码逻辑遵循先判断,后写入变量,再进行外部调用的编码规范(Checks-Effects-Interactions)会使项目更加安全稳定。NUMEN 拥有专业的安全团队,为 WEB3 生态保驾护航。

【免责声明】市场有风险,投资需谨慎。本文不构成投资建议,用户应考虑本文中的任何意见、观点或结论是否符合其特定状况。据此投资,责任自负。
来源链接

