bHOME 重入攻击事件分析

创宇区块链安全实验室
// 前言
    北京时间 3 月 5 日,知道创宇区块链安全实验室 监测到 BaconProtocol 遭受黑客攻击损失约 958,166 美元,本次攻击利用重入漏洞,并凭借闪电贷扩大收益额。目前攻击者地址还没有被加入 USDC 的黑名单中。
    // 分析
    攻击事件如下图所示,该次攻击事件的问题点在于lend()函数,攻击者利用该函数进行重入攻击。
    
    2.1 基础信息
    攻击合约:0x580cac65c2620d194371ef29eb887a7d8dcc91bf
    攻击者地址:0x7c42f2a7d9ad01294ecef9be1e38272c84607593
    攻击 tx:0x7d2296bcb936aa5e2397ddf8ccba59f54a178c3901666b49291d880369dbcf31
    漏洞合约:0x781ad73f140815763d9A4D4752DAf9203361D07D
    2.2 流程
    1.攻击者通过 Uniswap V2 闪电贷借出 6,360,000 USDC。
    
    2.用闪电贷借出的 6,360,000 USDC 分成 3 份,去 bHOME 中进行2次重入攻击调用了3次lend()函数铸造共 8,465,943.180104 bHOME。
    
    3.将刚才重入获得的 bHOME 拿去销毁赎回获得 7,336,924.998 USDC。
    
    4、归还闪电贷借出的 6,360,000 USDC,并支付 19,716 的手续费,最后将剩余的 957,208.998 USDC 转入自己的账户。
    
    2.3 细节
    该次攻击事件重点在于 lend() 函数,由于合约 https://etherscan.io/address/0x781ad73f140815763d9a4d4752daf9203361d07d#code 并未验证开源,所以我们只能从交易的 Debugger 中寻找线索。
    1、攻击者通过 Uniswap V2 闪电贷借出 6,360,000 USDC。
    
    2、用闪电贷借出的 6,360,000 USDC 分成 3 份,去 bHOME 中进行重入攻击 2 次调用 lend()函数铸造共 8,465,943.180104 bHOME。
    
    3、将刚才重入获得的 bHOME 拿去销毁赎回获得 7,336,924.998 USDC。
    4、归还闪电贷借出的 6,360,000 USDC,并支付 19,716 的手续费,最后将剩余的 957,208.998 USDC 转入自己的账户。
    
    2.3 细节
    该次攻击事件重点在于 lend() 函数,由于合约 https://etherscan.io/address/0x781ad73f140815763d9a4d4752daf9203361d07d#code 并未验证开源,所以我们只能从交易的 Debugger 中寻找线索。
    
    分析 Debugger 可以看到下图是第一次调用函数,接着进行了 2 次重入。
    
    
    从重入攻击中可以找到 INPUT 中调用函数的字节码为 0xa6aa57ce
    
    通过字节签名可以知道重入攻击调用的是 lend() 函数。
    
    从 Debugger 中我们可以发现攻击合约的地址 0x781ad73f140815763d9a4d4752daf9203361d07d
    
    通过反编译合约 0x781ad73f140815763d9a4d4752daf9203361d07d 得到合约伪代码内容,可以找到 lend() 函数(465 - 550 行)。
    
    我们在反编译的代码中可以看到该合约使用的 ERC777 协议,会查询 recipient 的回调合约并回调 tokensReceived() 函数用以重入攻击。
    
    通过分析可以发现 _index 就是传入的 USDC 数量。
    
    totalSupplybalanceOf[caller] 的增量都和 _index 是正比关系,和 stor104 是反比关系,并且这两个变量的更新发生在重入攻击之前,每次都会更新。
    
    stor104 记录的是用户存入的 USDC 总量,该变量的更新发生在重入之后,那么在重入的过程中 stor104 的值是不变的,而上面的 totalSupply 是在变大,所以最后 return 的值也会相应的增大,从而使得攻击者通过重入攻击铸造得到比正常逻辑更多的 bHOME 代币。
    
    除了该次攻击事件,BlockSec Team 还阻断了后面发生的攻击并将金额返还给了项目方。交易 hash:0xf3bd801f5a75ec8177af654374f2901b5ad928abcc0a99432fb5a20981e7bbd1
    
    3. 总结
    此次攻击事件是由于项目方使用了 ERC777 协议但没有对回调合约的安全性进行考虑从而导致了重入攻击的发生,当项目合约有涉及资产的转移时,建议使用「检查-生效-交互」模式来保证逻辑代码的安全,当然我们也可以使用 OpenZeppelin 官方提供的 ReentrancyGuard 修饰器来防止重入攻击的发生。