// 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이 뜬다.. 애초에 컨트랙트에 적은 양이 있어서 그런게 아닐까 싶다.
