以小博大 —— UniLend 被黑事件分析
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 尚未从池中转出,导致健康因子计算结果高于实际情况,系统错误地认为用户的借贷状态是安全的。攻击者因此能够绕过健康因子的正确校验,非法获取目标资产。慢雾安全团队建议项目方在健康因子计算过程中,确保资产状态的实时更新,从而避免类似情况的发生。
本文由币圈网发布,不代表币圈网立场,转载联系作者并注明出处:https://www.brcns.cn/news/bkhelebn.html