Resupply 黑客事件分析

Resupply黑客事件分析

Resupply 黑客事件分析

名词解释

  1. borrow:借出资产,在这里指 reUSD。是 resupply 协议的借出资产。
  2. colateral:抵押物,在这里指 wstUSRResupply Vault 金库合约中的存款凭证。
  3. underlying:底层资产,在这里指 crvUSD,属于 Curve 协议的稳定币。整套闭环的最底层资产依赖。
  4. LTVloan to value 抵押率。例如70%则为抵押 100u 最多能借出 70u

resupply 项目整体流程图

image-20250629171016073.png 我这里简单分析下 Resupply 协议的运作方式,如果有错误,请指正。

这是一个经典的 Defi 组合场景,流动性质押 + 金库质押 + 借贷协议 三重协议来达到一鱼多吃的效果。具体表现为:

  1. 流动性质押:用户在 Curve 这个 DEX 项目中提供流动性,赚取去中心化交易所的手续费收益和额外的奖励。在提供 crvUSD 这个稳定币作为流动性的同时,Curve 会给流动性提供者 mintLP 代币,即 cvcrvUSD,作为将来如果流动性提供者想要换回原资产的凭证。

  2. 金库质押:由于 cvcrvUSD 也是一种资产(只要其他项目认可的情况下),所以 cvcrvUSD 又可以通过存款的方式放入 Resupply 的金库协议中,作为一种质押代币所存在。同时,在 Resupply Vault 的金库协议中,也会给存款者 mint 出相应份额的 wstUSR 作为凭证,同样是作为将来取回 cvcrvUSD 而存在的。在金库合约中,金库本身会用用户存款的 cvcrvUSD 去做投资,进而达到 wstUSR 升值或者给用户分发 CVX 等的一些代币作为回报。

  3. 借贷:由于 wstUSR 也是一种资产,所以 wstUSR 又可以通过抵押进 ResupplyPair 中,去通过借贷协议借出稳定币 reUSD。而借出的 reUSD 作为一种稳定币,又能被用户拿去其他地方做投资使用。

这里描述的是正常的 Defi 循环场景,下面我将来简单分析下这次 Resupply 被盗事件中的协议漏洞及黑客是如何盗取资金的。

漏洞还原

借贷合约

image-20250629185437669.png

在用户的借款中,每次借款都会去调用 isSolvent 函数修饰器,去检查这个用户有没有资格借款。这次的漏洞就出现在这个 isSolvent 函数修饰器中。

image-20250629171341092.png

具体进到修饰器中看,这里主要是进行了一个判断:判断用户总借款金额是否超过上限。

例如:

总借款上限 maxLTV 假设为 85%,则每次用户调用借款都会去判断 LTV 是否超过 85%。图上主要依靠的计算公式为:((借款总额 * 汇率)) / 抵押物金额。这个公式为图上的简化。

由此可知,当用户每次借款时,借款总额都会累加,直到 LTV 超过 maxLTV

那么,问题来了,假如在这个借款的过程中,_exchangeRate(汇率)是可以被操控的话,让 _exchangeRate 恒等于 0 或者极接近于 0。那么用户则会一直能借出 reUSD 且都是大额的借出。

此次黑客事件也是基于这个汇率做文章。

image-20250629172142826.png

那么黑客是如何进行汇率的操控的呢?继续追踪下去,可以看到,在这个合约中,汇率的计算是通过 汇率 = 1e36/ 抵押品价格

如果要让这个 _exchangeRate 等于 0 (除法下溢可以做到)或者非常接近于 0,则这个抵押品(wstUSD)的价格要足够大(price 代表了一个 wtUSR 可以借出多少 reUSD)。

则此时黑客需要操控的 price (分母)变得很大很大。

而在合约协议中,这个 price 是通过预言机合约调用 getPrice 函数去获取的。那么我们继续追踪下去。

预言机合约

image-20250629195132136.png

在预言机合约中,实际上并没有做什么事情,仅仅是做了一次金库合约的调用转发。

那么此时问题就来了,预言机对于价格的转发高度依赖 Vault 金库合约中的 _convertToAsset 函数,而不是真实市场上的价格(链下预言、喂价)。假如黑客有能力操控 Vault 金库的价格,那他就能操控借贷合约去实现 _exchangeRate 等于 0 或者接近于 0 的目标,进而攻击这个漏洞。

实际上,本次黑客攻击也的确是这样干的。具体是如何实现的,可以看下面金库合约

金库合约

image-20250629182636024.png

继续追踪到金库 Vault 合约中,我们可以看到在 _convertToAsset 函数中,实际是做了一次资产的换算,也就是计算在金库中的质押份额乘上总的金库资产。

那么这里就有一个问题,假如这个金库资产是空的金库(total_asset = 0, totalSupply = 0),或者资产的深度很小。那么黑客可以通过绕过 deposit 函数,而是直接打钱(捐赠)的方式让 total_asset 总资产变得很大,在 shares 不变(仍为 0,但黑客可以 deposit 极少量的 cvcrvUSD 进去,使得 shares 不为 0)的情况下,使得这个函数返回的值变得很大。进而达到攻击的目的。

就假如说,这个金库是一个接收 cvcrvUSD ,返回 wstUSR 的金库。那么根据这里的计算,黑客给这个空金库捐赠大额 cvcrvUSD。然后黑客 deposit 存入很少的 wstUSR 即可算出少量 wstUSR 可以换出大量 cvcrvUSD

又因为这个金库实际上是个空的金库,只有黑客在操作,所以这个返回值变得很大了后,黑客可以可以让前面的汇率接近 0 或者等于 0 了。进而达到攻击的目的了。

攻击路径解析

  1. 黑客通过 Resupply 协议部署一个抵押 wstUSR 借出 reUSD 的新借贷合约。其中这个借贷合约依赖了 Resupply Vault 合约的 wstUSR 的汇率。
  2. 黑客直接通过捐赠的方式,往依赖的 Vault 金库中的打入大量 cvcrvUSD 资产。然后黑客通过正常 deposit 的方式存入很少量的 cvcrvUSD,获取极少的 wstUSR。又因为这个金库中实际上是个空金库,所以此时 wstUSRcvcrvUSD 的汇率极高。仅一点点 wstUSR 可以换取大量 cvcrvUSD。(实际外部市场上汇率没这么高)
  3. 经过上面操控 Vault 市场的动作,使得借贷合约中 exchangeRate = 1e36 / price = 0(被整除下溢),所以导致借贷合约中_isSolvent() 被判定为安全,TVL 始终等于 0
  4. isSolvent 一直返回 true,借贷合约可以被无限借出(不超限额的情况下)。则黑客此时可以用很小的 wstUSR 换取大量的 reUSD。直到达到上限。

总结

核心原因是 Resupply 这个借贷协议太傻逼了,高度依赖 Vault 金库合约中的局部市场价格汇率。要是有空池检测或者真实市场预言机就这种漏洞根本不会发生。这就是我觉得 ChainLink 的代币值得观察的原因,真实世界预言机对于区块链生态而言真的太重要了。

参考

  1. 推主:@junairez https://x.com/junairez/status/1938598062869692473
  2. 攻击交易:https://app.blocksec.com/explorer/tx/eth/0xffbbd492e0605a8bb6d490c3cd879e87ff60862b0684160d08fd5711e7a872d3…
  3. ResupplyPair 合约地址:https://etherscan.io/address/0x6e90c85a495d54c6d7e1f3400fef1f6e59f86bd6#code…
  4. 金库 controller 合约地址:https://etherscan.io/address/0x89707721927d7aaeeee513797a8d6cbbd0e08f41#code…
  5. 金库合约地址:https://etherscan.io/address/0x01144442fba7aDccB5C9DC9cF33dd009D50A9e1D#code…
  6. 预言机合约地址:https://etherscan.io/address/0xcb7E25fbbd8aFE4ce73D7Dac647dbC3D847F3c82#code
点赞 0
收藏 0
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

请先 登录 后评论
shawn_shaw
shawn_shaw
web3潜水员、技术爱好者、web3钱包开发工程师。欢迎闲聊唠嗑、精进技术、交流工作机会。vx:cola_ocean