[Ethernaut CTF] Re-entrancy

0xDave·2022년 10월 4일
0

Ethereum

목록 보기
31/112

소스코드


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

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

contract Reentrance {
  
  using SafeMath for uint256;
  mapping(address => uint) public balances;

  function donate(address _to) public payable {
    balances[_to] = balances[_to].add(msg.value);
  }

  function balanceOf(address _who) public view returns (uint balance) {
    return balances[_who];
  }

  function withdraw(uint _amount) public {
    if(balances[msg.sender] >= _amount) {
      (bool result,) = msg.sender.call{value:_amount}("");
      if(result) {
        _amount;
      }
      balances[msg.sender] -= _amount;
    }
  }

  receive() external payable {}

해결과제


The goal of this level is for you to steal all the funds from the contract.

  Things that might help:

    Untrusted contracts can execute code where you least expect it.
    Fallback methods
    Throw/revert bubbling
    Sometimes the best way to attack a contract is with another contract.
    See the Help page above, section "Beyond the console"

컨트랙트의 돈을 모두 탈취하자!

해결과정


유명한 Re-entrancy 문제다. 먼저 돈을 기부하고 내가 보낸 이더만큼 계속 빼내면 될 것 같다.

첫 번째 시도

처음에 짠 컨트랙트. donate을 하려고 했더니 다음과 같이 에러가 뜬다.


두 번째 시도

donate 함수를 변경해서 직접 value를 넣어줬다. donate은 되지만 withdraw가 안 된다. for문 말고 다른 방법이 필요할 것 같다.

세 번째 시도

고수의 컨트랙트를 참고했다. 일단 re-entrancy 공격을 하는데 fallback 함수 없이 컨트랙트를 짜는 것은 완전 잘못된 방법이었다. fallback 함수를 추가하고, donatewithdraw를 한 번에 묶어서 함수로 만들었다. 첫 번째 시도에 비하면 장족의 발전이다.

donate은 되지만 withdraw가 out of gas 에러가 나면서 계속 revert 된다. 메타마스크 서포트 글을 읽어보니 트랜잭션에 사용된 gas가 설정된 gas limit을 넘어갈 때 발생한다고 한다. gas limit을 올리고, value도 올려보자.

네 번째 시도

wei도 많이 올리고 gaslimit도 10배 가량 높였다. 그런데 결과는 똑같았다. 컨트랙트에 얼마나 있는지 보려고 balanceOf를 조회해봤더니 0이 뜬다.. 애초에 컨트랙트에 적은 양이 있어서 그런게 아닐까 싶다.

profile
Just BUIDL :)

0개의 댓글