[Ethernaut CTF] Recovery

0xDave·2022년 10월 8일
0

Ethereum

목록 보기
36/112
post-thumbnail

소스코드


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

import '@openzeppelin/contracts/math/SafeMath.sol';

contract Recovery {

  //generate tokens
  function generateToken(string memory _name, uint256 _initialSupply) public {
    new SimpleToken(_name, msg.sender, _initialSupply);
  
  }
}

contract SimpleToken {

  using SafeMath for uint256;
  // public variables
  string public name;
  mapping (address => uint) public balances;

  // constructor
  constructor(string memory _name, address _creator, uint256 _initialSupply) public {
    name = _name;
    balances[_creator] = _initialSupply;
  }

  // collect ether in return for tokens
  receive() external payable {
    balances[msg.sender] = msg.value.mul(10);
  }

  // allow transfers of tokens
  function transfer(address _to, uint _amount) public { 
    require(balances[msg.sender] >= _amount);
    balances[msg.sender] = balances[msg.sender].sub(_amount);
    balances[_to] = _amount;
  }

  // clean up after ourselves
  function destroy(address payable _to) public {
    selfdestruct(_to);
  }
}

해결과제


A contract creator has built a very simple token factory contract. 
Anyone can create new tokens with ease. 
After deploying the first token contract, the creator sent 0.001 ether to obtain more tokens. 
They have since lost the contract address.

This level will be completed if you can recover (or remove) the 0.001 ether from the lost contract address.

해결과정


이더리움에서 주소를 만드는 2가지 방법


  1. keccack256(RLP_encode(sender, nonce))
  2. CREATE2
    keccak256( 0xff ++ senderAddress ++ salt ++ keccak256(init_code))

RLP는 Recursive-length prefix의 약자로 임의의 길이를 가진 문자열과 배열을 인코딩하는 방법이다. 이 때 값과 길이에 대한 정보가 포함되어 있다. 이더리움 네트워크에서는 RLP 방식을 트랜잭션 정보와 state, receipt, DB 저장에 사용한다.

처음엔 주소 생성을 랜덤으로 하는 줄 알았다. 하지만 1번과 같은 방법을 사용해서 주소를 만든다는 것을 알면 주소를 분실했을 때 다시 찾을 수 있다. sender의 주소와 nonce 값만 알면 되기 때문. 사실 이러한 방법보다 더 쉬운 방법이 있다. 바로 이더스캔을 보면 된다. 트랜잭션을 보면 어떤 주소가 생성됐는지 쉽게 알 수 있다.

Payload

컨트랙트에게 어떤 함수를 실행할지, 인자에 어떤 값을 넣어줄지 알려주는 역할을 한다. 예를 들어, 컨트랙트에서 아래 함수를 실행한다고 해보자.

transfer(address,uint256)

이더리움에서는 함수명과 인자를 keccack256에 넣어서 나온 첫 4바이트를 통해 어떤 인자를 가지고 어떤 함수를 실행하는지 파악할 수 있다. 위 예에서는 0xa9059cbb가 첫 4바이트다. 0x337c67618968370907da31dAEf3020238D01c9de 주소로 10000개의 토큰을 보낸다고 했을 때, 각각의 정보는 32바이트로 바뀐 다음 합쳐진다. 주소는 0x000000000000000000000000337c67618968370907da31dAEf3020238D01c9de로 바뀌고 토큰 갯수는 0x0000000000000000000000000000000000000000000000008ac7230489e80000로 변환된다. 각 정보에서 16진수를 나타내는 0x를 제외한 후, 함수와 인자에 대한 정보까지 합쳐져서 최종 payload는 다음과 같다.

a9059cbb000000000000000000000000337c67618968370907da31dAEf3020238D01c9de0000000000000000000000000000000000000000000000008ac7230489e80000

selfdestruct

이더스캔으로 알아낸 주소를 인자로 넣어서 selfdestruct 함수를 실행하면 컨트랙트가 파괴되면서 안에 있던 토큰을 주소로 보낼 수 있다.

출처 및 참고자료

  1. https://ethereum.stackexchange.com/questions/3584/what-is-the-use-of-the-payload-field-in-the-ethereum-transaction-reciept
profile
Just BUIDL :)

0개의 댓글