根据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生态保驾护航。