cairo-vulnerability-scanner について
Cairo/StarkNetコントラクトのセキュリティレビューを支援し、算術、レイヤー間メッセージ、署名に関する6種類の脆弱性パターンを確認します。監査担当者やリリース前の評価チーム向けに、証拠と修正案を含むパターン別の結果を提示します。
主な機能
- 016種類のCairo/StarkNet脆弱性を確認
- 02L1-L2メッセージとアドレス検証を点検
- 03署名リプレイ対策を確認
- 04証拠付きのパターン別カバレッジ報告
- 05検出項目の修正案を提示
活用シーン
- 01StarkNetスマートコントラクトの監査
- 02L1-L2ブリッジ実装のレビュー
- 03リリース前の署名検証ロジック評価
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ trailofbits/ skills --skill cairo-vulnerability-scannerインストール後、Claude Code で/cairo-vulnerability-scannerを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
agents/openai.yamlassets/trail-of-bits-mark.svgresources/VULNERABILITY_PATTERNS.md詳細
Cairo/StarkNet 脆弱性スキャナー
1. 目的
StarkNet 上の Cairo スマートコントラクトを体系的にスキャンし、算術演算、クロスレイヤーメッセージング、暗号操作に関連するプラットフォーム固有のセキュリティ脆弱性を検出します。このスキルには、Cairo/StarkNet エコシステム特有の6つの重大な脆弱性パターンが含まれています。
2. このスキルを使用する場面
- StarkNet スマートコントラクト(Cairo)の監査
- L1-L2 ブリッジ実装のレビュー
- StarkNet アプリケーションのローンチ前セキュリティ評価
- クロスレイヤーメッセージ処理の検証
- 署名検証ロジックのレビュー
- L1 ハンドラー関数の評価
3. プラットフォームの検出
ファイル拡張子と識別子
- Cairo ファイル:
.cairo
言語/フレームワークの識別子
// Cairo contract indicators
#[contract]
mod MyContract {
use starknet::ContractAddress;
#[storage]
struct Storage {
balance: LegacyMap<ContractAddress, felt252>,
}
#[external(v0)]
fn transfer(ref self: ContractState, to: ContractAddress, amount: felt252) {
// Contract logic
}
#[l1_handler]
fn handle_deposit(ref self: ContractState, from_address: felt252, amount: u256) {
// L1 message handler
}
}
// Common patterns
felt252, u128, u256
ContractAddress, EthAddress
#[external(v0)], #[l1_handler], #[constructor]
get_caller_address(), get_contract_address()
send_message_to_l1_syscall
プロジェクト構成
src/contract.cairo- メインのコントラクト実装src/lib.cairo- ライブラリモジュールtests/- コントラクトテストScarb.toml- Cairo プロジェクト設定
ツールのサポート
- Caracal:Cairo 用 Trail of Bits 静的アナライザー
- インストール:
cargo install --git https://github.com/crytic/caracal --profile release --force(Rust ツールのため、PyPI では配布されていません) - 使用方法:
caracal detect src/ - cairo-test:組み込みのテストフレームワーク
- Starknet Foundry:テストおよび開発ツールキット
4. このスキルの仕組み
実行されると、次の処理を行います。
- コードベースを検索して Cairo ファイルを探します
- 各コントラクトを分析し、6つの脆弱性パターンを確認します
- 検出結果を報告し、ファイル参照と重大度を示します。その上に、各パターンの判定を記載したカバレッジ表を表示します
- 特定された各問題の修正方法を提示します
- メッセージングの脆弱性がないか、L1-L2 間のやり取りを確認します
5. 出力例
脆弱性が見つかった場合、次のようなレポートが表示されます。
=== CAIRO/STARKNET VULNERABILITY SCAN RESULTS ===
6. 脆弱性パターン(6パターン)
Cairo/Starknet 特有の重大な脆弱性パターンを6つ確認します。詳細な検出パターン、コード例、緩和策、テスト戦略については、VULNERABILITY_PATTERNS.md を参照してください。
パターンの概要:
- Felt252 の算術オーバーフロー/アンダーフロー ⚠️ HIGH -
felt252は暗黙にラップするため、u128/u256を使用します - L1 から L2 へのアドレス変換 ⚠️ HIGH - L1 アドレスが STARKNET_FIELD_PRIME と照合して検証されていません
- L1 から L2 へのメッセージ失敗 ⚠️ HIGH - メッセージを消費できない場合のキャンセル経路がありません
- 過度に制約された L1 <-> L2 間のやり取り ⚠️ MEDIUM - 資金を動かせなくしたり、処理を妨げたりする可能性のある結合
- 署名のリプレイ保護 ⚠️ HIGH - ノンスがない、またはドメインセパレーターにチェーン/コントラクトが含まれていません
- L1 ハンドラーの未検証の from_address ⚠️ CRITICAL - あらゆる L1 コントラクトがハンドラーを実行させる可能性があります
コード例を含む脆弱性パターンの全容については、VULNERABILITY_PATTERNS.md を参照してください。
7. スキャンのワークフロー
ステップ1:プラットフォームの特定
- Cairo 言語と StarkNet フレームワークを確認します
- Cairo のバージョンを確認します(Cairo 1.0 以降か、旧 Cairo 0 か)
- コントラクトファイル(
src/*.cairo)を特定します - L1-L2 ブリッジコントラクトがあれば特定します
ステップ2:算術安全性の確認
# Find felt252 usage in arithmetic
rg "felt252" src/ | rg "[-+*/]"
# Find balance/amount storage using felt252
rg "felt252" src/ | rg "balance|amount|total|supply"
# Should prefer u128, u256 instead
ステップ3:L1 ハンドラーの分析
各 #[l1_handler] 関数について:
-
from_addressパラメーターを検証している - アドレスがゼロでないことを確認している
- 適切なアクセス制御がある
- 監視用のイベントを発行している
ステップ4:署名検証のレビュー
署名ベースの関数について:
- ノンスを追跡している
- 使用後にノンスをインクリメントしている
- ドメインセパレーターにチェーン ID とコントラクトアドレスが含まれている
- 署名をリプレイできない
ステップ5:L1-L2 ブリッジの監査
コントラクトにブリッジ機能が含まれている場合:
- L1 がアドレス < STARKNET_FIELD_PRIME を検証している
- L1 がメッセージのキャンセルを実装している
- L2 がハンドラー内の from_address を検証している
- L1 ↔ L2 間で対称的なアクセス制御がある
- 一連のラウンドトリップ処理をテストする
ステップ6:Caracal による静的解析
# Run Caracal detectors
caracal detect src/
# Specific detectors
caracal detect src/ --detectors unchecked-felt252-arithmetic
caracal detect src/ --detectors unchecked-l1-handler-from
caracal detect src/ --detectors missing-nonce-validation
8. レポート形式
カバレッジ表
§6 のすべてのパターンについて、該当するものが見つかったかどうかにかかわらず報告します。調査結果の前にこの表を出力し、 6 行すべてを含めてください。
| # | パターン | 判定 | 根拠 |
|---|---|---|---|
| 1 | Felt252 の算術オーバーフロー/アンダーフロー | clear | 残高は u256 です。算術処理で felt252 を検索しましたが、該当なし |
| 2 | L1 から L2 へのアドレス変換 | ||
| 3 | L1 から L2 へのメッセージ失敗 | ||
| 4 | 制約過多の L1 <-> L2 インタラクション | ||
| 5 | 署名のリプレイ保護 | ||
| 6 | L1 ハンドラーでの未検証の from_address |
判定には次のいずれかを指定します。
found—file:lineを示し、以下に問題の詳細をすべて記載します。clear— このパターンはこのコントラクトに該当しますが、コントラクトで対処されています。読者が同じ検索を再現できるよう、検索した関数、トレイト、またはチェックを記載します。n/a— このパターンはここでは該当しません。理由を 1 つの節で示します(「このコントラクトには L1 ハンドラーがない」など)。調査していないことはn/aの理由になりません。
6 行未満の表はスキャンが不完全であり、そのように報告する必要があります。Verdict のセルが空の行も同様に不完全です。上の 1 行目は形式を示すために記入済みであり、レポートを完了する前にすべての行を同じように記入してください。6 件すべてが clear という結果も、読者が対応に活用できます。2 つのパターンだけを扱い、残りの 4 つに触れないレポートは、問題のないコントラクトとまったく同じように見えます。この表はその失敗を防ぐためにあります。
問題の記載テンプレート
## [CRITICAL] Unchecked from_address in L1 Handler
**Location**: `src/bridge.cairo:145-155` (handle_deposit function)
**Description**:
The `handle_deposit` L1 handler function does not validate the `from_address` parameter. Any L1 contract can send messages to this function and mint tokens for arbitrary users, bypassing the intended L1 bridge access controls.
**Vulnerable Code**:
```rust
// bridge.cairo, line 145
#[l1_handler]
fn handle_deposit(
ref self: ContractState,
from_address: felt252, // Not validated!
user: ContractAddress,
amount: u256
) {
let current_balance = self.balances.read(user);
self.balances.write(user, current_balance + amount);
}
```
**Attack Scenario**:
1. Attacker deploys malicious L1 contract
2. Malicious contract calls `starknetCore.sendMessageToL2(l2Contract, selector, [attacker_address, 1000000])`
3. L2 handler processes message without checking sender
4. Attacker receives 1,000,000 tokens without depositing any funds
5. Protocol suffers infinite mint vulnerability
**Recommendation**:
Validate `from_address` against authorized L1 bridge:
```rust
#[l1_handler]
fn handle_deposit(
ref self: ContractState,
from_address: felt252,
user: ContractAddress,
amount: u256
) {
// Validate L1 sender
let authorized_l1_bridge = self.l1_bridge_address.read();
assert(from_address == authorized_l1_bridge, 'Unauthorized L1 sender');
let current_balance = self.balances.read(user);
self.balances.write(user, current_balance + amount);
}
```
**References**:
- building-secure-contracts/not-so-smart-contracts/cairo/unchecked_l1_handler_from
- Caracal detector: `unchecked-l1-handler-from`
9. 優先度のガイドライン
クリティカル(即時修正が必要)
- L1 ハンドラーでの未検証の from_address(無限発行)
- L1-L2 アドレス変換の問題(資金がゼロアドレスに送られる)
高(デプロイ前に修正)
- Felt252 の算術オーバーフロー/アンダーフロー(残高の操作)
- 署名のリプレイ保護の欠如(リプレイ攻撃)
- キャンセル機能のない L1-L2 メッセージ失敗(資金のロック)
中(監査で対処)
- 制約過多の L1-L2 インタラクション(資金の滞留)
10. テストの推奨事項
ユニットテスト
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_felt252_overflow() {
// Test arithmetic edge cases
}
#[test]
#[should_panic]
fn test_unauthorized_l1_handler() {
// Wrong from_address should fail
}
#[test]
fn test_signature_replay_protection() {
// Same signature twice should fail
}
}
統合テスト(L1 を使用)
// Test full L1-L2 flow
#[test]
fn test_deposit_withdraw_roundtrip() {
// 1. Deposit on L1
// 2. Wait for L2 processing
// 3. Verify L2 balance
// 4. Withdraw to L1
// 5. Verify L1 balance restored
}
Caracal CI の統合
# .github/workflows/security.yml
- name: Run Caracal
run: |
# Rebuilds from source each run; cache ~/.cargo or pin a release binary instead.
cargo install --git https://github.com/crytic/caracal --profile release --force
caracal detect src/ --fail-on high,critical
11. 追加リソース
- セキュアなコントラクトの構築:
building-secure-contracts/not-so-smart-contracts/cairo/ - Caracal:https://github.com/crytic/caracal
- Cairo ドキュメント:https://book.cairo-lang.org/
- StarkNet ドキュメント:https://docs.starknet.io/
- OpenZeppelin Cairo コントラクト:https://github.com/OpenZeppelin/cairo-contracts
12. クイックリファレンスチェックリスト
Cairo/StarkNet の監査を完了する前に確認してください。
算術の安全性(高):
- 残高や金額に
felt252を使用していない(u128/u256を使用する) - または、
felt252の算術処理に明示的な境界チェックがある - オーバーフロー/アンダーフローのシナリオをテスト済み
L1 ハンドラーのセキュリティ(クリティカル):
- すべての
#[l1_handler]関数でfrom_addressを検証している - from_address を保存済みの L1 コントラクトアドレスと照合している
- 別の L1 コントラクトをデプロイしても回避できない
L1-L2 メッセージング(高):
- L1 ブリッジでアドレスが STARKNET_FIELD_PRIME 未満であることを検証している
- L1 ブリッジにメッセージのキャンセル機能がある
- L2 ハンドラーで from_address をチェックしている
- L1 ↔ L2 の検証ルールが対称的である
- 一連の往復フローをテスト済み
署名のセキュリティ(高):
- 署名に nonce の追跡が含まれている
- 使用のたびに nonce をインクリメントしている
- ドメインセパレーターにチェーン ID とコントラクトアドレスが含まれている
- 署名のリプレイをテストし、防止している
- クロスチェーンのリプレイを防止している
ツールの使用:
- Caracal のスキャンが完了し、クリティカルな問題が見つかっていない
- ユニットテストで、すべての脆弱性シナリオを網羅している
- 統合テストで、L1-L2 フローを検証している
- メインネットの前にテストネットへのデプロイをテスト済み
- 6 行すべてを含むカバレッジ表を出力し、各行に理由を添えて
found、clear、またはn/aの判定を記載している
13. 退けるべき合理化
- 「コントラクトは小さいので、ほとんどのパターンは明らかに該当しません。」 誰にとって明らかなのでしょうか。
n/aは1つの節で済み、判断をレビュー可能にします。何も書かなければ記録は残らず、読者は確認していない場合と区別できません。 - 「Caracal は何も報告しなかったので、コントラクトに問題はありません。」 Caracal が対象とするのはこれら6つのパターンの一部であり、ロジックレベルのパターンにはまったく対応していません。ツールの実行結果が問題なしでも、それは証拠の1行にすぎず、調査していないパターンに対する判定ではありません。どのパターンを対象にしたのかを明記してください。
- 「このコントラクトで重要なパターンは確認しました。」 どのパターンが重要かを決めることがスキャンそのものであり、開始前の条件ではありません。行を省くのではなく、表を完成させてから重大度順に並べてください。
- 「検出事項はなかったので、報告することはありません。」 検出事項がゼロのスキャンでも、網羅性を示す表全体を出力します。その表が成果物です。調査されたコントラクトと、ざっと目を通しただけのコントラクトを区別するためのものです。
- 「Cairo 1 にはネイティブのオーバーフローチェックがあるので、算術は安全です。」 型を具体的に挙げてください。
felt252はサイズ付き整数型のようには動作せず、両者の境界でパターン4が発生します。 - 「L1 側で検証しています。」 それなら L1 コントラクトを引用してください。ブリッジを介して検証されるはずだと考えているチェックは、実際に確認するまでは仮定にすぎません。未検証の
from_addressは、StarkNet のブリッジにおける典型的なバグです。