攻击过程
0x25126......403907(hETHWBTC pool)
0x5a63e......844e74(攻击合约 - 套现)
借出初始闪电贷资金,进行质押
攻击者首先从 Aave 闪电贷借入 2300WBTC,将其中 2000 枚 WBTC 质押(deposit)到 HopeLend,资金将会被转移至 HopeLend 的 hEthWbtc 合约(0x251…907),同时获取相应的 2000 枚 hETHWBTC。

借助空借贷池操纵初始贴现率(liquidityIndex)
从 HopeLend 进行闪电贷借入 2000 枚 WBTC。

目前的价值是 1 hETHWBTC = 1 WBTC。
按照正常的存取 ETHWBTC 换回 WBTC 的操作,是不会影响兑换比例(只有当收入了利息才会影响兑换比例,1 hETHWBTC 会获得更多 WBTC)。
此时黑客开始通过一系列复杂操作,操纵贴现率:
- 黑客直接又将得到的 2000 枚 WBTC 通过直接转账(transfer)的方式转移资金至 HopeLend 的 hEthWbtc 合约 ( 0x251…907),这一步并不是还贷。
- 黑客随后取出(withdraw)之前步骤 1 中质押(deposit)的绝大部分 WBTC(1999.999…),所以上一步才需要转回 WBTC 以补充池子内的资产。
- 最后黑客手上仅保留最小单位 (1e-8) 的 hEthWbtc,这里不能完全提完,是因为需要留下一点点,作为计算贴现率(liquidityIndex)时,会基于现有的加上新增的,如果清零的话,导致贴现率(liquidityIndex)变成 0,就不能让池子里的比例失衡。
- 把上一步销毁掉绝大部分 hEthWbtc 换回来的 wBTC,加上之前闪电贷剩余的 wBTC,归还向 HopeLend 池子借出的闪电贷,共支付 2001.8 枚 WBTC( 其中包含利息 1.8 枚 wBTC)。
- 上面的过程销毁掉大部分的 hEthWbtc,只留下 1 最小单位(1e-8)的 hEthWbtc 在黑客账户,这样一来 hETHWBTC 总量就减少了,而借贷池里却有 2001.8 枚 wBTC,此时的贴现率(liquidityIndex)达到惊人的 126,000,000。
这里涉及到一个知识,存款用户的利息根本上来自池中流动性的增长,借贷池会根据存款率和使用率,动态调节借款和存款利率。

此处,当池子从闪电贷利息 (1.8WBTC) 获得额外流动性时,百分之七十 (126,000,000) 被计入 liquidityIndex(liquidityIndex),这个数值用来计算每单位存款 (hEthWbt) 的贴现价值。


由于池子在黑客操作前为空,还款后 totalLiquidity 仅为 1,amount 是 126000000,初始 liquidityIndex 为 1,得出结果为 126000001。

继续放大贴现率
黑客继续从 HopeLend 进行闪电贷借入 2000 枚 WBTC,并每次归还额外的 1.8 枚 WBTC,使得每次 LiquidityIndex 得以累加 126,000,000。
黑客重复执行了 60 次该过程,最终 liquidityIndex 达到 7,560,000,001,攻击者持有的 1 个最小单位的 hEthWBTC 贴现价值可达 75.6WBTC(约为 214 万美元)。
这也就使得黑客操控了 hEthWBTC,使之价值失真。

掏空其他存在资金的借贷池,形成收益
攻击者接着将 1 个最小单位的 hEthWBTC 为抵押,从 HopeLend 的其他五个代币池借出了大量资产。
包括:
- 175.4 - WETH
- 145,522.220985 - USDT
- 123,406.134999 - USDC
- 844,282.284002229528476039 - HOPE
- 220,617.821736563540747967 - stHOPE

这些代币被作为收益通过 Uniswap 兑换为 WBTC 和 WETH,扣除各种费用后,最终黑客获利为约 263 枚 WETH(除去贿赂 payload 的 263.9 枚 WETH)。
为什么黑客可以从其他池子借走大量资金:
借款或取走存款时,借贷合约会检验用户的抵押资产状况,确保借出不超过抵押。
由于之前贴现率已被黑客操纵且贴现率会以 normalizedIncome 乘数计入抵押价值计算,其手中的一单位 hEthWBTC 抵押价值高达 75.6WBTC。


每一次从其他池借款,黑客都轻松通过了抵押资产校验。

此时, 攻击者总共在 HopeLend 投入了 2000+1.8*60 枚 WBTC 用于操纵 liquidityIndex,只留存了 1 单位的 hEtthWBTC。
利用关键漏洞点(整数除法错误)套现
为了取出之前的投入 wBTC,攻击者部署了另一个攻击合约:0x5a63e......844e74,并调用其中的 withdrawAllBtc() 方法:

漏洞过程如下:
- 首先存入 151.20000002 枚 wBTC,根据当前的 liquidityIndex(1 最小单位 hEthWBTC=75.6wBTC),攻击者获得 2 个最小单位的 hEthWBTC。
- 取出(withdraw)113.4 个 wBTC,反算出其对应的 hEthWBTC 份额,对 hEthWBTC 进行 burn 操作。
- 113.4 个 wBTC 需要销毁 1.9999999998 最小单位的的 hEthWBTC,但是由于 div 函数精度问题,仅一个最小单位的 hEthWBTC 被销毁,因此变成可被利用的漏洞,黑客仍可保留 1 个最小单位的 hEthWBTC。