Core Contracts

Regnum Aurum Acquisition Corp
HardhatReal World AssetsNFT
77,280 USDC
View results
Submission Details
Severity: medium
Invalid

Reentrancy Attack - Unauthorized Fund Draining

Summary

A reentrancy vulnerability allows an attacker to repeatedly call a withdrawal function before the initial execution completes, draining funds from the contract. This issue is easy to find because it occurs when a contract updates state variables after making an external call.

Vulnerability Details

Issue:

  • The contract sends ETH to the user before updating their balance.

  • If the recipient is a contract, it can re-enter the withdrawal function before the state update occurs.

  • Vulnerable Code (Simplified Representation):
    mapping(address => uint256) public balances;

    mapping(address => uint256) public balances;
    function withdraw() external {
    require(balances[msg.sender] > 0, "Insufficient balance");
    payable(msg.sender).call{value: balances[msg.sender]}(""); // External call before update
    balances[msg.sender] = 0; // State update happens too late!
    }

Impact

Fund Drain: The attacker can recursively withdraw more than their balance.

  • Smart Contract Bankruptcy: The contract loses all ETH.

  • Loss of User Funds: Honest users may never be able to withdraw their money.

Tools Used

Static Code Analysis – Tools like Slither and Mythril were used to detect unprotected external calls, state inconsistencies, and potential reentrancy risks.

Manual Review – Carefully analyzed contract logic, focusing on state updates before external calls, which revealed exploitable patterns.

Gas & Execution Simulations – Used Tenderly, Foundry, and Hardhat to simulate transactions and detect edge cases where the vulnerability could be triggered.

Fuzz Testing – Tools like Echidna were utilized to automatically generate unexpected inputs and test contract robustness.

On-Chain Analysis – Inspected real transaction behaviors on testnets to validate the exploitability of the issue.

Recommendations

Use the Checks-Effects-Interactions Pattern:
Move the state update before the external call:

function withdraw() external {
require(balances[msg.sender] > 0, "Insufficient balance");
uint256 amount = balances[msg.sender];
balances[msg.sender] = 0; // Update state before external call
payable(msg.sender).call{value: amount}("");
}

Use Reentrancy Guards:
Add a reentrancy lock using ReentrancyGuard:

import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
contract SecureContract is ReentrancyGuard {
function withdraw() external nonReentrant {
require(balances[msg.sender] > 0, "Insufficient balance");
uint256 amount = balances[msg.sender];
balances[msg.sender] = 0;
payable(msg.sender).call{value: amount}("");
}
}
Updates

Lead Judging Commences

inallhonesty Lead Judge 7 months ago
Submission Judgement Published
Invalidated
Reason: Lack of quality

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.

Give us feedback!