ironCTF 2024 Web 問題の解法考察:JWTからGit漏洩まで

はじめに

ironCTF 2024のWebセクションに挑戦した記録です。初の表彰台(チーム内)で非常に嬉しい経験となりました。一部の問題は知識重視の「唐」な出題もありましたが、学びが多くありました。

順位表

ユーザー名 スコア
LamentTyphon 2468
Jerrythepro123 2610
Dragonkeep 483

問題一覧

  • JWT Hunt
  • Mango
  • Loan App
  • b64SiteViewer
  • cerealShop
  • MovieReviewApp

JWT Hunt: シークレット復元チャレンジ

JWTトークンの署名鍵が4つの部分に分割され、それぞれ異なる場所に隠されていました。スキャナ使用禁止という制約がありましたが、手動探索が求められます。

  • パート1: /robots.txt 内に記載
  • パート2: HTTPレスポンスのCookieヘッダー
  • パート3: /sitemap.xml の中に存在(スキャナ不要だが発見が難しい)
  • パート4: 直接 curl /secretkeypart4 を実行して取得

全パートを結合すると、完全なシークレットキーが得られます:

6yH$#v9Wq3e&Zf8LpRt1%Y4nJ^aPk7Sd2C@mQjUwEbGoIhNy0T!BxlVz5uMKA#Yp

このキーを使って、以下のペイロードを持つ管理者用JWTを生成します:

{
  "username": "admin",
  "exp": 1728200262
}

最終的なトークンをCookieに設定しアクセスすることで、フラグを獲得。

フラグ: ironCTF{W0w_U_R34lly_Kn0w_4_L07_Ab0ut_JWT_3xp10r4710n!}

Mango: 誘導と誤解

タイトルからMongoDBインジェクションを連想させますが、実際は単純な罠でした。脆弱性テストを行う前に、/admin/index に直接アクセスすると、フラグが即座に表示されます。

フラグ: ironCTF{I_Said_M@nG0_Not_M0ngo!}

Loan App: HAProxy 組み合わせ攻撃

HAProxyの設定ファイルを確認すると、/admin パスへのアクセスがブロックされています。しかし、CVE-2021-40346 として知られるHTTPリクエストスメラッシュ(Request Smuggling)の脆弱性を利用可能です。

ただし、本問ではより簡便な別解が存在しました。UUID検証関数に関するStack Overflowの記事を検索し、特定の形式のUUID(例: fbd3036f-0f1c-4e98-b71c-d4cd61213f90)を入力すると、バックエンドのチェックを通過してフラグが返却されました。これは非予期されたショートカットでしたが、現実のCTFではよくあることです。

b64SiteViewer: SSRF + RCE 環境構築

提供されたFlaskアプリケーションにはSSRFおよびRCEの脆弱性があります。特に/adminエンドポイントは、ループバックアドレスからのアクセスに限りOSコマンド実行が可能。

SSRFフィルタを回避するため、IPv4アドレス127.0.0.1を16進表記0x7f.0.0.1に変換して利用します。

RCE側では、コマンド名にシングルクォートやダブルクォートが含まれないよう制限されています。また、cmd_blacklistにより特定の文字列もブロックされます。このフィルタはバックスラッシュによるエスケープで簡単にバイパス可能です。

以下のURLにアクセス:

http://0x7f.0.0.1:5000/admin?cmd=en\\v

Base64エンコードされた応答をデコードすると、環境変数に格納されたフラグが含まれています。

フラグ: ironCTF{y0u4r3r0ck1n6k33ph4ck1n6}

cerealShop: PHP反シリアル化脆弱性

ソースコードは?file=../../../../../sourceで取得可能。PHPのunserialize()関数を使用しており、Adminクラスに__toString()マジックメソッドが定義されています。

目標は、is_admin == 0かつyour_secret === my_secretという条件を満たすオブジェクトを送信することです。ここで、is_adminはmd5ハッシュですが、比較時に整数0と評価される文字列(例: "0")を使うことで、文字列と整数の弱い比較(==)を悪用できます。

さらに、your_secretmy_secretを同じ参照(ポインタ)にすることで、値の一致を保証します。以下のようなPoCコードでペイロードを生成します:

<?php
class Admin {
    public $is_admin = "0";
    public $your_secret;
    public $my_secret;

    public function __construct() {
        $this->my_secret = 'dummy';
        $this->your_secret = &$this->my_secret; // 参照で連結
    }
}

echo base64_encode(serialize(new Admin()));
?>

得られたBase64文字列をcan_you_get_meという名前のCookieに設定し、ページを再読み込みするとフラグが表示されます。

フラグ: ironCTF{D353r1411Z4710N_4T_1T5_B35T}

MovieReviewApp: .gitディレクトリからの情報収集

サイトルートに.gitディレクトリが公開されており、git clonegit logなどの操作が可能。履歴を確認すると、過去のコミットに管理者の認証情報がプレーンテキストで含まれていました。

ADMIN_USERNAME=admin_user
ADMIN_PASSWORD=secure_pass_123

これらの資格情報を使用して/adminにログイン後、/admin_panelに移動します。ここにはping機能があり、内部でsubprocess.check_output()が使われています。

IPアドレスは正規表現で厳密に検証されていますが、countパラメータにはサーバーサイドのバリデーションがありません。Burp Suiteなどでリクエストを捕捉し、count1; cat /flag.txtに書き換えます。

これによりOSコマンドインジェクションが成功し、フラグを取得できます。

フラグ: ironCTF{4lways_b3_c4ar3ful_w1th_G1t!}

タグ: CTF Webセキュリティ JWT MongoDB SSRF

7月28日 20:38 投稿