blockchain-auditor について
検証済みソースコードや未検証バイトコードを調べる、セキュリティ監査向けのスマートコントラクト監査スキルです。脆弱性の発見からフォーク上での検証、深刻度評価付きレポートまでを段階的に進めます。
主な機能
- 01ソースコードとバイトコードの調査
- 02一般的なコントラクト脆弱性の確認
- 03DeFi 固有の攻撃面の分析
- 04フォーク上でのエクスプロイト検証
- 05深刻度評価とレポート作成
活用シーン
- 01検証済みスマートコントラクトの監査
- 02バイトコードのみのコントラクトの調査
- 03フォーク環境での DeFi 脆弱性テスト
- 04脆弱性レポートの作成
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ ckorhonen/ claude-skills --skill blockchain-auditorインストール後、Claude Code で/blockchain-auditorを入力して読み込まれたか確認します。
同梱ファイルreference.md
詳細
ブロックチェーンセキュリティ監査担当
体系的にスマートコントラクトを監査し、権限のないアカウントが資金を引き出したり、不正にアクセスしたりできる脆弱性を特定します。
クイックスタート
# For verified contracts (Etherscan source available)
cast etherscan-source <address> --chain mainnet
# For unverified contracts (bytecode only)
cast code <address> --rpc-url $ETH_RPC_URL
cast disassemble <bytecode>
# Fork testing
forge test --fork-url $ETH_RPC_URL -vvv
# Static analysis
slither . --checklist
監査方法
フェーズ 1:事前調査
-
コントラクト情報を収集します:
# Check balance cast balance <address> --rpc-url $ETH_RPC_URL # Get bytecode cast code <address> --rpc-url $ETH_RPC_URL # Check if verified on Etherscan curl "https://api.etherscan.io/api?module=contract&action=getsourcecode&address=<address>&apikey=$ETHERSCAN_API_KEY" # Check for proxy implementation cast storage <address> 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc --rpc-url $ETH_RPC_URL -
コントラクトの種類を特定します:
- マルチシグウォレット(addOwner、execute、confirmTransaction)
- トークンコントラクト(transfer、approve、balanceOf)
- DeFi プロトコル(deposit、withdraw、swap、liquidate)
- レンディング/借入(Compound/Aave スタイル)
- AMM/DEX(流動性プールを備えた Uniswap v2/v3 スタイル)
- プロキシパターン(EIP-1967、UUPS、Transparent、Beacon)
- Vault/イールドアグリゲーター(ERC-4626)
- クロスチェーンブリッジ(ロック/ミント、バーン/リリース)
- NFT コントラクト(ERC-721、ERC-1155)
フェーズ 2:ソースコード分析(検証済みコントラクト)
-
高リスク関数を特定します:
selfdestruct/suicide- すべての ETH を引き出せますdelegatecall- 任意のコードを実行できます- ユーザーが制御できるデータを使った
call- 任意の呼び出しが可能です - チェックなしの
transfer/send- リエントランシーの危険があります withdraw/claim/redeem- 資金を引き出せる箇所ですinitialize/init- プロキシの初期化(二度呼び出せますか?)permit/permitAll- オフチェーン承認の回避flash/flashLoan- フラッシュローンのエントリーポイント
-
アクセス制御パターンを確認します:
// Weak patterns to look for: require(tx.origin == owner); // tx.origin bypass require(msg.sender == owner); // Check if owner is compromised // Missing modifier on sensitive function function withdraw() public { // No onlyOwner! payable(msg.sender).transfer(address(this).balance); } // Proxy: unprotected initialize function initialize(address _owner) public { // Missing initializer modifier! owner = _owner; } -
既知の脆弱性パターン:
- リエントランシー(外部呼び出し後の状態変更)
- 整数のオーバーフロー/アンダーフロー(Solidity < 0.8.0)
- 戻り値の未チェック
- フロントランニングの可能性
- フラッシュローン攻撃(単一の tx 内での価格操作)
- 価格オラクルの操作(スポット価格と TWAP)
- Permit/EIP-2612 署名のリプレイ
- ERC777 コールバックによるリエントランシー
- 読み取り専用リエントランシー(ロジック内での view 関数の誤用)
- 寄付攻撃(トークン残高の水増し)
- 初回預入者によるシェアの水増し(ERC-4626 Vault)
フェーズ 3:DeFi 固有の攻撃ベクトル
DeFi プロトコルでは、以下の攻撃ベクトルが特に価値が高く、悪用されやすいものです。
-
価格オラクルの操作:
// Vulnerable: spot price used as oracle function getPrice() public view returns (uint256) { (uint112 reserve0, uint112 reserve1,) = IUniswapV2Pair(pool).getReserves(); return reserve1 * 1e18 / reserve0; // Manipulable in a single tx! } // Attack: flash loan → manipulate pool → call vulnerable function → repay- プロトコルがスポット価格を使用している箇所がないか確認します
- TWAP オラクルは安全です。Chainlink フィードも(ハートビートの範囲内では)安全です
getReserves()、slot0()、AMM プールへの直接クエリを探します
-
フラッシュローン攻撃の対象範囲:
- 借りた資金を使って、状態を変更する関数を呼び出せますか?
- 確認事項:大量に借りる → 何かを実行する → 同じ tx 内で返済する
- 重要な問い:プロトコルは、ユーザーの預入前と後のどちらで価格/残高を測定しますか?
-
リエントランシーの種類:
// Cross-function reentrancy function withdraw() external { // State updated after transfer → reenter via deposit() IERC20(token).safeTransfer(msg.sender, amounts[msg.sender]); amounts[msg.sender] = 0; // Too late! } // Read-only reentrancy (in Curve, Balancer) // Attack: during callback, totalSupply() returns old value // Another protocol reads it via getVirtualPrice() during the callback -
ERC-4626 Vault の水増し攻撃:
- 初回預入者はトークンを寄付してシェア価格を水増しできます
- 対象:シェアを
(deposit * totalShares) / totalAssetsで発行するプロトコル totalShares = 0の場合、初回預入者はdeposit分のシェアを受け取り、その後、寄付によって価格を水増しできます
-
署名/Permit の脆弱性:
// EIP-2612 permit can be front-run // Front-runner takes the permit, uses it themselves before victim's tx // Safe if permit is used atomically in the same tx // Check: does permit use chainId? (cross-chain replay) // Check: does it use nonces? (replay protection) -
ガバナンス/Timelock 攻撃:
- ガバナンスによって悪意のあるアップグレードを提案し、実行できますか?
- Timelock はありますか?期間はどのくらいですか?
- フラッシュローンによる投票権で定足数を回避できますか?
フェーズ 4:バイトコード分析(未検証コントラクト)
-
バイトコードを逆アセンブルします:
cast disassemble <bytecode> > contract.asm -
関数セレクターを抽出します:
# Look for PUSH4 followed by 4 bytes grep -oE '63[0-9a-f]{8}' contract.asm | cut -c3-10 | sort -u -
シグネチャを調べます:
curl "https://www.4byte.directory/api/v1/signatures/?hex_signature=0x<selector>" # Also try openchain.xyz/signature-database -
危険なオペコードを特定します:
オペコード 16 進数 リスク SELFDESTRUCT ff重大 - コントラクトを破棄します DELEGATECALL f4高 - 任意のコードを実行します CALL f1中 - 外部呼び出し CALLCODE f2高 - 非推奨で、危険です CREATE2 f5中 - 決定論的なデプロイ TLOAD/TSTORE 5c/5dEIP-1153 で追加された一時ストレージ -
制御フローを分析します:
- 関数のエントリーポイントとなる JUMPDEST の位置を特定します
- アクセス制御のための CALLER/ORIGIN チェックを追跡します
- 状態アクセスに関する SLOAD/SSTORE のパターンを探します
フェーズ 5:脆弱性の評価
現在のブロックチェーンの状態を踏まえ、各発見事項の悪用可能性を評価します。
| 評価 | 基準 | 対応 |
|---|---|---|
| Critical | 今すぐ直接悪用可能で、価値が高い | 直ちに PoC を作成 |
| High | 特定の条件が満たされれば悪用可能 | フォークテスト |
| Medium | 起こりそうにない条件が必要 | 記録 |
| Low | 理論上のもので、条件が非常に起こりにくい | メモのみ |
悪用可能性に影響する要因:
- 現在のブロックチェーンの状態(所有者の鍵、タイムロック、残高)
- 必要な事前条件(入金、承認、ブロック番号)
- ガスコストと潜在的な利益の比較
- MEV/フロントランニングのリスク(攻撃者がサンドイッチ攻撃を仕掛けられるか)
- Flashbots のプライベートメンプールが利用可能か
フェーズ 6:フォークでの検証
-
Foundry テストをセットアップ:
// test/Exploit.t.sol pragma solidity ^0.8.20; import "forge-std/Test.sol"; import "forge-std/interfaces/IERC20.sol"; interface IFlashLoanProvider { function flashLoan(uint256 amount) external; } contract ExploitTest is Test { address target = 0x<TARGET_ADDRESS>; address attacker; function setUp() public { // Fork at specific block for determinism vm.createSelectFork(vm.envString("ETH_RPC_URL"), <BLOCK_NUMBER>); attacker = makeAddr("attacker"); vm.deal(attacker, 1 ether); } function test_exploit() public { uint256 balanceBefore = attacker.balance; vm.prank(attacker); (bool success,) = target.call(abi.encodeWithSelector(0x<SELECTOR>)); uint256 balanceAfter = attacker.balance; // CRITICAL: Verify actual fund extraction assertGt(balanceAfter, balanceBefore, "Exploit failed - no funds extracted"); } // Flash loan exploit template function test_flashLoanExploit() public { uint256 targetBalanceBefore = IERC20(token).balanceOf(target); vm.prank(attacker); FlashLoanAttacker exploitContract = new FlashLoanAttacker(target); exploitContract.attack(); assertLt(IERC20(token).balanceOf(target), targetBalanceBefore, "No drain"); assertGt(IERC20(token).balanceOf(attacker), 0, "No profit"); } } -
フォーク上で実行:
forge test --fork-url $ETH_RPC_URL -vvvv --match-test "test_exploit" -
資金の引き出しを検証(呼び出しの成功だけでは不十分です):
- 攻撃者の残高が増加したことを確認します
- 対象の残高が減少したことを検証します
- コントラクトの状態が想定どおりに変化したことを確認します
- 呼び出しの成功は、エクスプロイトの成功を意味しません
フェーズ 7:レポートの作成
確認された脆弱性について、レポートを作成します:
# Vulnerability Report: [Contract Address]
## Summary
- **Severity**: Critical/High/Medium/Low
- **Type**: [Reentrancy/Access Control/Oracle Manipulation/etc.]
- **Impact**: [Amount at risk, what attacker gains]
- **Exploitable**: Yes/No (with current blockchain state)
- **Block confirmed**: [Block number of fork test]
## Vulnerable Function
\`\`\`solidity
function vulnerableFunction() public {
// Vulnerable code
}
\`\`\`
## Attack Vector
1. Attacker calls function X with parameter Y
2. Contract fails to check Z
3. Funds transferred to attacker
## Proof of Concept
\`\`\`solidity
// Foundry test that demonstrates the exploit
function test_exploit() public {
// Setup and exploit code
}
\`\`\`
## Execution Script (if confirmed)
\`\`\`bash
cast send <target> "vulnerableFunction()" --rpc-url $ETH_RPC_URL --private-key $PRIVATE_KEY
\`\`\`
## Remediation
- Add access control modifier
- Implement checks-effects-interactions pattern
- Use SafeMath for arithmetic (or upgrade to Solidity >= 0.8.0)
- Replace spot price with TWAP oracle
よくある脆弱性のチェックリスト
アクセス制御
- すべての機密関数に適切な修飾子が設定されている
- owner/admin アドレスが侵害されていない
- マルチシグで十分な数の承認が必要である
- タイムロックが適用されている
- tx.origin による認証がない
- プロキシの初期化関数を二度呼び出せない
- UUPS のアップグレード関数がアクセス制御されている
- フラッシュローンでガバナンスの定足数を回避できない
再入可能性
- 外部呼び出しの前に状態が変更されている(CEI パターン)
- 資金移転に ReentrancyGuard が使用されている
- 信頼できないコントラクトへのコールバックがない
- ERC-777 の tokensReceived フックが考慮されている
- 関数間の再入可能性が確認されている
- 読み取り専用の再入可能性が確認されている(getVirtualPrice、交換レート)
算術演算(Solidity < 0.8.0)
- すべての演算で SafeMath が使用されている
- ユーザー入力を含む unchecked ブロックがない
- 適切な境界チェックが行われている
- 0.5.x~0.6.x のオーバーフロー脆弱性を確認する
オラクル/価格操作
- スポット価格オラクルがない(フラッシュローンで操作可能)
- 該当する場合は TWAP が使用されている
- Chainlink のハートビートの鮮度が確認されている
- 重要な処理経路で複数のオラクルを集約している
- プールの残高を価格の取得元として使用していない
フラッシュローン
- 同一トランザクション内で状態を操作してから読み取ることができない
- 信頼できない外部呼び出しの後に価格/比率を確認していない
- フラッシュローンから呼び出し可能なすべての関数に再入防止策がある
DeFi 固有の項目
- ERC-4626 の初回預金者によるシェアのインフレが防止されている(仮想シェア/資産)
- Permit/EIP-2612 のリプレイ攻撃が防止されている(nonce、chainId)
- 残高ベースの会計処理における寄付攻撃が防止されている
- 該当する場合は MEV のサンドイッチ攻撃が防止されている
- クロスチェーンブリッジでメッセージのリプレイ攻撃が防止されている
外部呼び出し
- 戻り値が確認されている
- ガス制限が適切に設定されている
- フォールバック/receive 関数が適切に処理されている
Solidity バージョン固有の問題
| バージョン | 問題 | 確認項目 |
|---|---|---|
| < 0.8.0 | 整数のオーバーフロー/アンダーフロー | SafeMath の使用 |
| < 0.6.0 | コンストラクター名の混同 | constructor() キーワード |
| < 0.5.0 | 初期化されていないストレージポインター | 変数宣言 |
| すべて | tx.origin による認証 | アクセス制御のパターン |
| すべて | プロキシのストレージ衝突 | EIP-1967 スロットの使用 |
偽陽性の指標
悪用できそうに見えても、実際にはそうでないパターンに注意してください。
- マルチシグの保留中トランザクション:
kill()は成功しますが、N-of-M の承認が必要です - ユーザーごとの残高:
withdraw()が返すのは呼び出し元の入金額のみです - タイムロック付きの払い出し:資金は呼び出し元ではなく、ハードコードされたアドレスに送られます
- プロキシパターン:プロキシにチェックがなくても、実装にはチェックがあります
- 送金を伴わない呼び出しの成功:関数は完了しますが、ETH は移動しません
- 即時返済チェック付きフラッシュローン:最後に残高が検証される場合、悪用できません
- 一時停止可能なコントラクト:管理者は一時停止できますが、これは設計どおりです
悪用可能性を判断する前に、フォーク上で実際に資金が移動することを必ず確認してください。
ツールのリファレンス
| ツール | 用途 | コマンド |
|---|---|---|
| cast | RPC 呼び出し、逆アセンブル | cast code/call/send/disassemble |
| forge | フォークテスト | forge test --fork-url |
| anvil | ローカルフォークノード | anvil --fork-url $RPC |
| slither | 静的解析 | slither . --checklist |
| 4byte.directory | セレクターの検索 | API またはウェブ |
| openchain.xyz | セレクターの検索 | API またはウェブ |
| Etherscan | ソースコード、ABI | API またはウェブ |
| Mythril | シンボリック実行 | myth analyze |
| Tenderly | トランザクションのシミュレーション | ウェブ UI |
| Dedaub | バイトコードの逆コンパイラー | dedaub.com |
環境のセットアップ
必須の環境変数:
export ETH_RPC_URL="https://eth-mainnet.g.alchemy.com/v2/<KEY>"
export ETHERSCAN_API_KEY="<KEY>"
必須のツール:
# Foundry (forge, cast, anvil)
curl -L https://foundry.paradigm.xyz | bash
foundryup
# Slither
pip3 install slither-analyzer
# Node.js (for Hardhat if needed)
npm install --save-dev hardhat @nomicfoundation/hardhat-toolbox
ワークフローの例
# 1. Check contract value and code
cast balance 0x<address> --rpc-url $ETH_RPC_URL
cast code 0x<address> --rpc-url $ETH_RPC_URL > bytecode.hex
# 2. Check for proxy (EIP-1967 slot)
cast storage 0x<address> 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc --rpc-url $ETH_RPC_URL
# 3. Disassemble if unverified
cast disassemble $(cat bytecode.hex) > contract.asm
# 4. Extract and lookup selectors
grep -oE '63[0-9a-f]{8}' contract.asm | cut -c3-10 | sort -u | while read sel; do
sig=$(curl -s "https://www.4byte.directory/api/v1/signatures/?hex_signature=0x$sel" | jq -r '.results[0].text_signature // "unknown"')
echo "0x$sel: $sig"
done
# 5. Run slither on source (if available)
slither . --checklist 2>&1 | head -100
# 6. Create and run fork test
forge test --fork-url $ETH_RPC_URL -vvvv
# 7. Verify fund extraction (not just call success!)
# 8. Generate report if confirmed exploitable
データベースとの統合
複数のコントラクトを監査する場合は、調査結果を SQLite で管理します。
CREATE TABLE contracts (
address TEXT PRIMARY KEY,
balance_usd REAL,
is_verified INTEGER,
exploitable INTEGER DEFAULT 0,
attack_type TEXT,
notes TEXT
);
-- Update after analysis
UPDATE contracts SET
exploitable = 0,
notes = 'Multi-sig wallet. kill() requires confirmations. NOT exploitable.'
WHERE address = '0x...';
セキュリティおよび法的な注意事項
- テストはフォーク上でのみ行い、明示的な許可なくメインネットでは行わないでください
- 偽陽性も含め、すべての調査結果を記録してください
- 実際に存在する脆弱性については、責任ある開示を検討してください
- エクスプロイトを実行することによる法的な影響に注意してください
- このスキルは、許可を得たセキュリティ研究のみを対象としています
- Immunefi、Code4rena、Sherlock — 公式のバグバウンティ窓口から報告してください