Medium Severity
Published: Nov 8, 2026

[GitHub] php2shell-poc

Other
Type
N/A
CVSS Score
1
Views
Anonymous
Author

Description

# php2shell — PHP 8.4.26 Session Vulnerability Reproduction

[中文](#中文) | [English](#english)

---

## English

Under the constraints of "completely remote, HTTP-only, target page has no `session_start()`, no PHP written on target side," use **a single `exploit.py`** to achieve RCE on PHP 8.4.26 + Apache prefork + mod_php. The target page contains only `<?php echo "OK\n"; ?>`.

### Supported Scope (Read First)

> **The only supported runtime environment is the official `php:8.4.26-apache` image** (PHP 8.4.26 / Debian 12 / Apache prefork + mod_php) combined with configurations in the [docker/](docker/) directory of this repository.

This is not a generic ASLR bypass, nor is ASLR disabled:

- The exploitation process leaks only **Zend MM chunk base** and **libphp base** through two out-of-bounds reads; libc, stack, or complete memory maps are not leaked.
- The address space remains randomized by ASLR. Host kernel ASLR (kASLR), container runtime, kernel `mmap` behavior, allocator, and specific PHP/Apache builds all affect heap layout and leak results.
- Heap geometry, calibration values, and libphp offsets in the chain are empirically measured for the official image and do not include address scanning or brute-force enumeration. After changing images, PHP builds, or distributions, even if addresses can still be leaked, the subsequent object forgery layout may not hold.

There are also hard requirements on the Apache side:

- Must be **prefork MPM + single worker + mod_php**. After a child process aborts, the parent process directly `fork`s a new worker without `exec`; thus libphp mappings and address space dependencies across stages are preserved.
- `docker/apache.conf` fixes `StartServers/MinSpareServers/MaxSpareServers/MaxRequestWorkers/ServerLimit = 1`, `MaxConnectionsPerChild = 0`, and disables KeepAlive.
- Multiple workers, event/worker MPM, Apache restart/graceful restart, worker `exec`, container rebuild, or parent process exit may re-randomize addresses or route requests to different processes, breaking the entire chain.
- If any stage's leak fails, ASLR is still effective; retrying only switches to a different worker and does not "defeat" ASLR.

Current RCE also requires: `display_errors=1`, `memory_limit=8M`, `max_input_vars=1000`, `session.use_cookies=1`, `session.use_strict_mode=0`, `session.upload_progress.enabled=1`.

### One-Command Reproduction

This repository does not compile PHP/Apache and has no `docker build`: the environment is the official `php:8.4.26-apache` image plus configurations under `docker/` mounted.

```bash
# Dependencies: Docker Engine/Desktop + Docker Compose v2 + Python 3.9+
git clone https://github.com/9bie/php2shell-bypass-aslr-exp.git
cd php2shell-bypass-aslr-exp

# 1) Pull the official image (PHP 8.4.26 / Debian 12 / Apache prefork)
docker compose -f docker/docker-compose.yml pull

# 2) Start target: single worker, display_errors=On, memory_limit=8M
docker compose -f docker/docker-compose.yml up -d

# 3) Wait for Apache to be ready and verify the normal page
for i in $(seq 1 20); do
curl -fsS http://127.0.0.1:8088/index.php >/dev/null 2>&1 && break
sleep 0.5
done
curl -s http://127.0.0.1:8088/index.php # OK

# 4) Complete the entire exploitation with one command; first run automatically reads libphp offsets
python3 exploit.py -u http://127.0.0.1:8088
```

Translated to English from Chinese. Show original · see the source links.
Community Rating
0

Login to rate this exploit

Quick Actions