// 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 함수를 추가하고, donate
과 withdraw
를 한 번에 묶어서 함수로 만들었다. 첫 번째 시도에 비하면 장족의 발전이다.
donate은 되지만 withdraw가 out of gas 에러가 나면서 계속 revert 된다. 메타마스크 서포트 글을 읽어보니 트랜잭션에 사용된 gas가 설정된 gas limit을 넘어갈 때 발생한다고 한다. gas limit을 올리고, value도 올려보자.
wei도 많이 올리고 gaslimit도 10배 가량 높였다. 그런데 결과는 똑같았다. 컨트랙트에 얼마나 있는지 보려고 balanceOf
를 조회해봤더니 0이 뜬다.. 애초에 컨트랙트에 적은 양이 있어서 그런게 아닐까 싶다.