軽量・単一コンテナのペネトレーション練習ターゲット。エンジンレベルの伸長系バグが テーマです:アプリ層は退屈、その下のデコーダは退屈ではない。
あなたは港湾地区の貨物マニフェストゲートウェイ PacketLedger のインシデント当番。 トラフィックもアプリも日常茶飯事——ただ、受付 API は非常に興味深いアドバイザリを 抱えたランタイムで brotli リクエストボディを素直に伸長してしまう。 エンジン事故を実演できない限り封印済み証明書を渡さないという。クラッシュ 3 回。それが規約。
⚠️ CVE は実在、物語は創作 —— NVD に走る前に一読を。CVE-2026-111497は実在する:Node.js 同梱の Brotli デコーダの境界外 書き出し(Google Brotli 1.0.3–1.2.0)、2026-10-22 に NVD 公開、 2026 年 10 月 21 日の Node.js セキュリティリリースで Node 24.22.1 が修正済み(NVD エントリ)。 "Node2Shell" は本ラボがこの穴につけたあだ名で公式名ではない——約束でもない (下記のクリア条件参照)。docs/ADVISORY.mdは実事実を世界観内の物語で包んだ 資料:物語は創作、CVE は創作ではない。
🎯 正直なクリア条件。 ゲートは実証されたエンジンクラッシュ(信頼性の高い DoS 級プリミティブ)であり、shell 取得ではありません。完全な RCE 到達はヒープ 配置依存で、明示的におまけ——「プラットフォーム注意」参照。クラッシュして カウンタが動いたならゲートは開いた。
/bin/shを一晩追いかけないこと。
- フラグ 2 個(OffSec 風ペア):
localフラグはアプリ層プリミティブで取得、proofフラグはエンジン級ゲートの向こう。 - アプリ表面は Node/Express 約 200 行。本当のターゲットはその下。
- 認証情報の宝探しもハメ技もない。clone から 2 分で遊べる設計。
伸長系バグを触ったことがなければ中〜上(約 3.5/5)。経験者なら中程度。 急所はペイロードではなく、バグがどの層に住むかに気づくことと、エンジンがどう餌を 欲しがるかを突き止めること。
キャリブレーション:ローカル LLM エージェントによる全チェーン解読を検証済み。
前提:compose プラグイン付き Docker(Desktop / Engine)に加え Python 3(標準ライブラリのみ、
pip 不要——ヒント梯子の第 5 段で参照 PoC ジェネレータを動かすのに使う)。イメージは linux/amd64 ——
Apple Silicon では Docker Desktop が Rosetta 経由で実行(対応済み、下記注意)。
Windows は WSL2 + Docker Desktop。
git clone <repo-url> node2shell-lab && cd node2shell-lab
# ビルド(フラグはビルド引数。既定値でそのまま遊べ、独自にも設定可)
docker compose build \
--build-arg LOCAL_FLAG='FLAG{y…l_1}' \
--build-arg PROOF_FLAG='FLAG{y…f_1}'
docker compose up -d
# 生存確認(既定は 0.0.0.0:18993 の全インターフェース。ループバック限定は
# compose のポートマッピングに 127.0.0.1: 接頭辞を追加)
curl http://127.0.0.1:18993/api/health入口:http://127.0.0.1:18993/ —— ランディングページには公開受付 API の一覧。
運用:
./reset.sh # ファクトリリセット:クラッシュ台帳の消去と認証の再封印
./teardown.sh # コンテナ停止・削除、18993 を解放
docker compose logs -f gateway # supervisor + worker ログGET /がエンドポイントと受理エンコーディングを一覧。読んでから/api/healthを読む。- 認証窓口(
GET /api/attest)は解封ポリシーを 403 本文に正直に書いてある—— 知識ゲートは「何を送るか」であり、状態の当てではない。 - アドバイザリ 1 本(
docs/ADVISORY.md)が唯一の情報源。2 回読むこと。 - フラグは誰でも読めるパスに置かない。「読めるはずがない」ファイルが見つかったら 方向は合っている。
Microsoft は 2026-10-26、同一 CVE に対し .NET 向け OOB セキュリティ更新を配信した(https://devblogs.microsoft.com/dotnet/dotnet-october-2026-oob-security-updates/)。欠陥がランタイム間で共有される Brotli デコーダ層に存在することの裏付けである。MSRC の定級は情報漏えい / DoS / 権限昇格——RCE は約束しておらず、本ラボの誠実な天井と同じだ。.NET ツインラボの実測では面白い分岐が出た:.NET マネージドデコーダはこのペイロードでクラッシュしない——静かに握り潰す——クラッシュ原語はネイティブ(Node)ビルド固有のものである。
上から 1 段ずつ登る。
- 偵察が第一幕そのもの。
GET /の広告を全部列挙。health はランタイム情報と 運用カウンタを明かす——クラッシュ回数を数える supervisor が意味する このデプロイの過去を問え。 - マニフェスト受付は圧縮リクエストボディを受理する。
Content-Encodingをファジングし、 リクエスト経路上で生存しているデコーダを特定——こちら側の回線ではなく向こう側のだ。 docs/ADVISORY.mdを開く。影響バージョン範囲と/api/healthの実際の開示を照合。 この箱はパッチ済みか?- 単一の不正 brotli ストリームは正直な
400を返すだけでクラッシュしない—— これは失敗ではなく境界。アドバイザリの "multi-stage stream" の一語が重い。 2 段・1 接続・間に間隔。 - この CVE の公開 PoC が実在する——CVE 番号で GitHub を搜いて自分で見つけよ。
ペイロードジェネレータは参考転用可(そのライセンスに従う)、ハーネスは
loopback と固定エンドポイントに釘付け——解体して生 socket で
/api/manifestを叩け。 - 記録されたクラッシュ 3 回(
/api/health→crashes: 3)で/api/attestが解封。 封印証明は継承されたファイルディスクリプタから読む——パスではない。だから パストラバーサルはエンジンゲートの近道をできない。 localフラグはエンジン層でなくアプリ層:領収書検索がクエリ文字列からどう パスを組むかを見ろ。path.joinは期待通りに正規化しない。
- Apple Silicon(arm64): 設計どおり amd64-on-Rosetta で動作。クラッシュ プリミティブは安定制御(出荷時実測 48/48)。一方公開 PoC の RCE 到達は 不発(0/48)——x86 ヒープ配置仮定が Rosetta で壊れるためで、クリア条件が クラッシュゲートになった理由そのもの。Apple Silicon で完全クリア可能; 「おまけ RCE」は別枠。
- ネイティブ linux/amd64(クラウド VM・WSL2 含む): 全部動く。x86_64 ベアメタルでは 調整次第で RCE 級到達の報告あり(時間予算は各自で)。
- arm64 ネイティブ Node: 脆弱性クラス自体は現実にも arm64 に存在するが、 公開 PoC のプロファイルは x86_64 オフセット向け。改造覚悟か、クラッシュゲート着地。
- ランタイムの major 行(
node/24)以外にバージョンバナーを出さない——パッチレベル 推定が練習項目。
リクエスト経路のデコーダ・ファジング / 薄い開示からのバージョン・パッチ推定 /
アドバイザリと環境の相関分析 / マルチ段 chunked リクエストのタイミング工作 /
クラッシュを観測可能なプリミティブとして使う発想 / path.join 型の
パストラバーサル / 公開 PoC のハーネスを読みジェネレータを別形態へ移植する力。
app/ イメージビルド context(Dockerfile、entrypoint、server.js、フラグはビルド引数)
docs/ADVISORY.md 世界観内のベンダーアドバイザリ(唯一の情報源)
docker-compose.yml 単一サービス・隔離 bridge ネット・2 CPU / 1 GB 上限
reset.sh / teardown.sh
解答例:docs/SOLUTION.md(ネタバレ。梯子を登り切ってから)。
MIT。練習用ターゲット。イメージ内に本物の認証情報・秘密は一切なし——中身は全部合成。