チャレンジ一覧
プログラミング - メモリダンプの解析
起動のたびに乱数で作られた合成メモリイメージ memory.img を解析します。先頭のヒープ管理表(先頭8バイト HEAPTBL + NUL・count(uint16)・エントリ(addr: uint32、size: uint16、flags: uint8、すべてリトルエンディアン))で live(flags=1)になっているアドレスの領域だけが現在の値で、解放済みの領域や管理表に載っていない残骸は古いデータです。各領域は `ALLOC:0x<4桁の小文字16進アドレス>:` + 長さ(uint16、リトルエンディアン)+ データで、データは MD5("heap-0x<4桁の小文字16進アドレス>") のダイジェストを繰り返し XOR すると `p<番号>=<4文字>` の断片に復号できます。live の断片を番号の昇順に4文字ずつつないだ40文字(小文字英数字)が答えです。答えを `python3 challenge.py '<答え>'` に渡すと、正解のときだけフラグが表示されます。
プログラミング - 既知平文攻撃
起動のたびに乱数で作られた証拠ファイル ciphertext.b64 と known.txt を解析します。ciphertext.b64 は観測所の報告書を、英小文字だけの短い鍵(長さは不明)で繰り返し XOR して Base64 にしたもので、known.txt は報告書の冒頭部分の平文です。報告書を復号すると `RESULT: unlock-code=...` という行があり、その unlock-code の値(16文字)が答えです。取り消された古いコードも書かれているので取り違えないでください。答えを `python3 challenge.py '<答え>'` に渡すと、正解のときだけフラグが表示されます。
プログラミング - 独自プロトコルの解析
protocol.bin は起動のたびに作られるバイナリのキャプチャです(/challenge にあります)。KINO で始まる長さつきのフレームがいくつか並んでいます。フレームを先頭から順にたどり、type=3 のフレームの payload を 0x23 で XOR 復号するとフラグが現れます。フラグは平文では置かれていません。
プログラミング - 画像ステガノグラフィ
image.pgm は起動のたびに作られるテキスト形式(P2)の PGM 画像です(/challenge にあります)。画素値の最下位ビット(LSB)を先頭から集め、8ビットずつ1バイトにまとめて読むと、NUL(0 のバイト)の手前までがフラグです。フラグは平文では置かれていません。
メモリから不審なネットワーク接続を見つけよ
侵害されたホスト `kinoco-app03` のメモリイメージから、ソケット一覧 `/challenge/netstat_dump.txt` と、ESTABLISHED のソケットを持つプロセスごとのヒープ領域 `/challenge/heap_carve/pid_<PID>.bin` を復元しました。 カーネルスレッドに偽装した不審なプロセス(インプラント)が外部の C2 サーバーと通信していて、ビーコン設定(フラグを含む)をヒープに、平文ではない形で隠し持っています。ソケット一覧からそのプロセスを特定し、ヒープから設定を取り出してください。ほかのヒープには、フラグに似たおとりの文字列が混ざっています。 使えるツール: python3, grep, strings, xxd, awk
メモリから実行コマンドを復元せよ
メモリ文字列の記録 `memdump.txt` を解析し、実行されたコマンド履歴を復元してください。Base64 文字列をデコードし、bash へ渡している不審なコマンドの引数からフラグを取得します。
削除されたSQLiteレコードを復元せよ
社内チャット「kinoco-msg」の SQLite データベース `/challenge/app.db` を渡します。「機密メッセージ」として送られた本文は、次の形で保存されます。 body = "enc:v1:" + hex(本文 XOR 鍵ストリーム) 鍵ストリーム = SHA-256("<salt>|<sender>|<sent_at>|0") || SHA-256("<salt>|<sender>|<sent_at>|1") || ... salt = settings テーブルの secure_msg_salt(sender と sent_at は、そのメッセージの列の値) 管理者がワンタイムのアカウントリカバリートークンを機密メッセージで送り、すぐに削除しました。SELECT では見えない削除済みのレコードを復元し、本文を復号してトークン(フラグ)を取り出してください。 使えるツール: sqlite3, python3, xxd, strings, grep
多重に圧縮されたファイルを解凍せよ
`/challenge/artifact.xz` は、攻撃者が持ち出そうとしていたファイルで、gzip・bzip2・xz・zip・tar が何重にも重ねてあります。形式を1層ずつ見極めながら剥がし、最も深いところにある `flag.txt` からフラグを取り出してください。 途中のアーカイブには、README・ノイズ・偽の `flag.txt` などのおとりが一緒に入っている階層があります。ファイルの拡張子はあてになりません。 使えるツール: file, gzip, bzip2, xz, zip/unzip, tar, python3
暗号化されたディスクを復号せよ
`/challenge/secret_disk.img` は本物の LUKS2 コンテナで、データ領域にはバックアップ(tar アーカイブ)が入っています。パスフレーズは弱いもので、`/challenge/wordlist.txt` のどれかです。 このターミナルでは device-mapper が使えず、`cryptsetup open` でボリュームを開けません。キースロットから取り出したボリュームキーで、データ領域を自分で復号し、中のファイルからフラグを見つけてください。 使えるツール: cryptsetup, openssl, python3, dd, tar
暗号解析 - RSA共通モジュラス攻撃
2つのシステムが同じRSAの法nを使い回し、公開指数だけを変えていました。同じメッセージを e1 と e2 で暗号化した暗号文 c1, c2 が漏れています(public_keys.txt、ciphertexts.txt)。nは素因数分解できませんが、秘密鍵なしでメッセージを取り戻し、フラグを提出してください。
暗号解析 - 小さい素数のDiffie-Hellman
古いIoT機器が36ビットしかない素数pでDiffie-Hellman鍵共有をしていました。handshake.txtには p, g, A = g^a mod p, B = g^b mod p と、共有鍵から作った鍵ストリームで暗号化された設定データがあります。離散対数を解いて共有鍵を求め、設定データに含まれるフラグを取り出してください。鍵ストリームの作り方はREADME.mdにあります。
音声ファイルに隠されたデータ
`hidden.wav` にはオンオフ変調(OOK)で符号化されたメッセージが隠されています。トーンのオン/オフをビット化してデコードし、フラグを取得してください。 参照用のデコーダは付いていません。標準ライブラリの `wave` モジュールなどで、自分で信号を読み解きます。 提供ファイル: hidden.wav
Androidアプリから秘密鍵を抽出せよ
安全なZIP形式APK fixture(evidence/app.apk)を展開し、Androidリソースに記録された暗号化済みAPIシークレットを解析してください。開発者がビルドに同梱してしまった秘密鍵のうち、どれがそのシークレットに対応するかを見極めて復号します。実端末や実アプリは使用しません。
CloudTrailログから不正アクセスを検出せよ
AWS CloudTrail の書き出し `/challenge/cloudtrail.json` を解析し、盗まれたアクセスキーによる不正アクセスと、シークレットの持ち出しを追ってください。 攻撃者は偵察・権限昇格・新しいアクセスキーの作成のあと、読み出したシークレットを、その新しいキーで外部バケットへ S3 PutObject として分割して送りました(CloudTrail は GetSecretValue の値を記録しません)。オブジェクトキーは `<prefix>/<連番4桁>-<16進>.part` で、16進は6バイトずつの暗号文です。連番順に連結したバイト列は、SHA-256("<新しいアクセスキーID>:<カウンタ>")(カウンタは0から始まる10進の文字列)を順に連結した鍵ストリームと XOR されています。 許可済みのレッドチーム演習(送信元 192.0.2.50)も同じ手口で記録されています。演習ではなく、実際のインシデントで持ち出されたデータを復元してください。 提供ファイル: cloudtrail.json
Dockerイメージレイヤーから秘密を抽出せよ
`/challenge/image/` は `docker save`(OCI レイアウト)の書き出しを展開したものです。レイヤーは `blobs/sha256/` にダイジェスト名の gzip 圧縮した tar として置かれ、順番は `manifest.json` から読みます。 `/root/.aws/credentials` は複数のレイヤーで書き換えられ、クリーンアップのレイヤーで whiteout(削除マーカー)され、その後に別の値で作り直されています。最終ビューからは消えている、whiteout で削除された時点の credentials を回収し、その `aws_secret_access_key` の値を答えてください。ほかの秘密ファイルや、削除前後の別の値はおとりです。
Python上級 - カスタムVMバイトコードの実行
program.kvmは独自の4レジスタVM用バイトコードです。READMEの命令仕様を基にPythonでインタプリタを実装し、VMの出力を復元してください。
TLS暗号化されたHTTPS通信を復号せよ
`~/evidence/` に、あるクライアント PC の HTTPS 通信を記録したキャプチャ `traffic.pcap`(libpcap 形式)と、ブラウザが書き出した TLS の鍵ログ `sslkeylog.txt`(NSS 形式)があります。鍵ログには、このキャプチャに含まれない通信の行も混ざっています。キャプチャ内の TLS 1.2 の通信を復号し、外部へ持ち出された情報(HTTP の POST の内容)からフラグを取得してください。通信は実際に傍受したものではなく、演習のために作った記録です。python3(cryptography)と openssl が使えます。
Web-26. User Profile Managerのプロトタイプ汚染
このNode.js/Express製アプリではログイン中のユーザーが自分のプロフィールを更新できます。更新処理に使われている再帰的なマージ関数が特殊なキーをきちんと除外しているか確認してみましょう。
Web-27. GraphQL APIのイントロスペクション漏洩
このGraphQL APIはUserオブジェクトのスキーマを公開しており、イントロスペクションが有効になっています。公式には案内されていないクエリやフィールドが存在しないか調べてみましょう。
Web-28. OAuth認可コードの横取り
このOAuth 2.0認可プロバイダーはログインフローを提供していますが、redirect_uriの検証が甘い可能性があります。認可コードを横取りして管理者権限のアクセストークンを得られないか確認してみましょう。
Web-29. MongoDBログインフォームのNoSQLインジェクション
このログインポータルはMongoDBを使っており、ユーザー名・パスワードをJSONとしてそのままクエリに組み込んでいます。パスワードを知らなくても管理者としてログインできないか確認してみましょう。
Web-30. KinoVaultの安全でないデシリアライズ
レガシーなバックアップシステム「KinoVault」は、ユーザーセッションをPythonのpickleでシリアライズしてCookieに保存していると噂されています。Cookieの形式を解析し、悪意あるpickleペイロードで権限を昇格できないか調べてみましょう。
ネットワーク解析 - HTTPのchunkedとgzipをTCPから復元
capture.pcapには、1本のTCP接続で2回行われたHTTPのやり取りが記録されています。サーバーからのセグメントは順番が入れ替わり、一部は再送で重複しています。2回目の応答(GET /api/export)はchunkedとgzipが重なっています。TCPストリームを組み立て直して応答を取り出し、管理者のAPIキー(フラグ)を見つけてください。
ネットワーク解析 - TCP再送差分の隠れチャネル
capture.pcapには同一TCPシーケンス番号を持つ再送パケットがあります。ペイロードが変化した再送だけを抽出し、同じシーケンス番号の2つの1バイトペイロードをXORして、シーケンス番号順にフラグを復元してください。