背景
2025 年 1 月 13 日,据慢雾 MistEye 安全监控系统监测,EVM 链上的 UniLend 遭攻击,损失约 19.7 万美元。慢雾安全团队对该事件展开分析并将结果分享如下:
(https://x.com/SlowMist_Team/status/1878651772375572573)
相关信息
攻击者地址:0x55f5f8058816d5376df310770ca3a2e294089c33
存在漏洞的合约地址:0xc86d2555f8c360d3c5e8e4364f42c1f2d169330e
攻击交易:0x44037ffc0993327176975e08789b71c1058318f48ddeff25890a577d6555b6ba
攻击核心
本次攻击的核心在于合约在赎回资产时,计算健康因子直接使用了池中旧的 USDC 余额。这导致健康因子的计算结果高于实际情况,使得系统错误地认为用户的借贷状态是安全的。攻击者利用这一漏洞,通过闪电贷借入大量资产,存入并赎回资产时绕过健康因子的正确校验,从而无需承担实际风险便获取了目标资产。
攻击流程
1. 事先质押资产:攻击者通过前置交易 (0xdaf42127499f878b62fc5ba2103135de1c36e1646487cee309c077296814f5ff) 提前质押 200 USDC 至 UnilendV2Pool,获得了 USDC lendShare 150,237,398。随后,攻击者转移 LP Token,为后续赎回资金作准备。
2. 利用闪电贷借入资产:攻击者通过闪电贷借入 60M USDC 和 5 wstETH,并将 wstETH 转换为 6 stETH。
3. 存入资产以获取借贷份额:攻击者分别两次调用 lend 函数,将 USDC 和 stETH 存入之前准备好的 LP 中,从而获得相应的份额。此时,攻击者持有的份额为:
4. 借入目标资产:由于攻击者事先存入了大量的 USDC,于是通过调用 borrow 函数,可以正常借入 60 stETH。此时由于借贷,stETH borrowShare 增加至 60239272000126842038。
5. 赎回质押的 stETH:攻击者调用 redeemUnderlying 函数,赎回质押的全部 stETH。由于攻击者从未借贷过 USDC,USDC borrowShare 为 0,因此可以直接赎回全部的 stETH,stETH lendShare 归零。
6. 赎回质押的 USDC:攻击者再次调用 redeemUnderlying 函数,赎回质押的全部 USDC。在 redeemUnderlying 函数中,首先调用 _burnLPposition 函数销毁对应的 USDC lendShare,此时的 USDC lendShare 还剩下 150237398。随后,合约在 checkHealthFactorLtv1 函数中检查健康因子,最后将赎回的 USDC 转移给用户。
理论上,攻击者已经凭借质押的 USDC 借出了一部分 stETH,当他想赎回全部的 USDC时,健康因子检查不应该通过。然而,实际情况并不是这样,让我们跟进 checkHealthFactorLtv1 函数:
我们很容易从上图发现,该函数先通过 userBalanceOftoken0 和 userBalanceOftoken1 函数获得当前的 USDC lendBalance 和 stETH borrowBalance,再计算 USDC 健康因子,并将其与安全阈值进行比较,以判断是否允许赎回 USDC。
继续深入 userBalanceOftoken0 函数进行检查:
显然,这就是问题的关键,合约直接使用了 pool 当前的 USDC 余额以及 USDC lendShare 来计算 lendBalance。然而,这一部分余额包含了用户准备赎回的 USDC 数量,USDC lendShare 却已经在前面的 _burnLPposition 函数中扣除了用户准备赎回的份额。因此 USDC lendBalance 由预期的 150237398*(728895404+4829907565)/4175666009 = 200001650 变为 150237398*(60000728895404+4829907565)/4175666009 = 2158955960717,明显偏大,导致健康因子的返回值远远高于预期,能成功通过校验。
7. 完成攻击并获利:最终,攻击者返还闪电贷借入的 USDC 和 wstETH,获利离场。由于漏洞,攻击者仅仅质押了 200 USDC 便可获得 60 stEth。
总结
本次攻击的核心在于攻击者利用 redeemUnderlying 函数使用了池子旧的 token 余额来计算健康因子,而此时用户的 token 尚未从池中转出,导致健康因子计算结果高于实际情况,系统错误地认为用户的借贷状态是安全的。攻击者因此能够绕过健康因子的正确校验,非法获取目标资产。慢雾安全团队建议项目方在健康因子计算过程中,确保资产状态的实时更新,从而避免类似情况的发生。