Skip to content
Open php2shell-hardening on GitHub

Open php2shell-hardening on GitHub

github.com • October 21, 2026

Unofficial, provisional edge rules to block multipart/form-data requests before they reach PHP, as a stopgap for CVE-2026-109618 / CVE-2026-109619 ("php2shell") while you wait to update PHP. Covers Nginx, Apache, ModSecurity and php.ini , with a test suite.

Read this first These rules are UNOFFICIAL and PROVISIONAL. As of 21 October 2026 PHP had not published the root cause, the affected versions, or the CVE-to-bug mapping. The link to multipart / session.upload_progress comes from a leaked PoC and third-party analysis, not confirmed by PHP. The real fix is to update PHP to the patched release. These rules buy time; they are not a substitute for the vendor patch. They block legitimate uploads and multipart forms. Apply them only to routes that do not need uploads, and test in staging first . A wrong or bypassable block rule in production does damage.

These rules are UNOFFICIAL and PROVISIONAL. As of 21 October 2026 PHP had not published the root cause, the affected versions, or the CVE-to-bug mapping. The link to multipart / session.upload_progress comes from a leaked PoC and third-party analysis, not confirmed by PHP.

The real fix is to update PHP to the patched release. These rules buy time; they are not a substitute for the vendor patch.

They block legitimate uploads and multipart forms. Apply them only to routes that do not need uploads, and test in staging first . A wrong or bypassable block rule in production does damage.

The leaked PoC ("php2shell") reaches PHP's multipart handling, including the session.upload_progress callback, before the application script runs , by sending a multipart/form-data POST (in most public PoCs a field named PHP_SESSION_UPLOAD_PROGRESS ). Blocking that request type at the edge, where you do not need it, removes the entry path. This is attribution from researchers, unverified by PHP.

Why the regex catches the comma variant

PHP lowercases the Content-Type and truncates it at the first ; , , or space before it matches a post handler, then looks for boundary anywhere in the header. So Content-Type: multipart/form-data,boundary=x is multipart to PHP . A rule that only matches multipart/form-data followed by ; or whitespace is bypassable with a comma. Sources (PHP 8.4.26):

method gate (only POST in sapi_activate ): main/SAPI.c#L463-L470

Content-Type lowercase + truncate at ; , : main/SAPI.c#L180-L197

boundary via strstr : main/rfc1867.c#L708

request_parse_body() reads the body by Content-Type with no POST gate (PHP 8.4+): ext/standard/http.c#L349

Per-route exceptions do not protect those routes. The multipart parser runs before your app code and before authentication, so any route you allow stays exposed.

session.upload_progress.enabled = Off is PHP_INI_PERDIR (so ini_set() cannot change it) and, per one researcher, closes only one of the two suspected bugs. Verify from the web SAPI with phpinfo() , not the CLI, then delete the test file. Reload PHP-FPM.

.htaccess can be silently ignored : insufficient AllowOverride , ProxyPassMatch / SetHandler proxy:fcgi setups, nested .htaccess with their own RewriteEngine , or a rule placed after an earlier [L] . Use %{HTTP:Content-Type} , not %{CONTENT_TYPE} .

PUT/PATCH : from PHP 8.4 request_parse_body() parses multipart on those methods too, so these rules include them.

Nginx + error_page cycle (fixed here). With error_page 403 ... + recursive_error_pages on (ISPConfig default), a plain if ... return 403; turns into HTTP 500 via an internal-redirect cycle. The $pp_errpage map (keyed on $uri ) plus volatile prevents it. Verified in production and on isolated nginx 1.24.

PHP 8.1 and older will not get an official fix. There these stopgaps may need to stay.

A WAF parses multipart differently from PHP. The ModSecurity rule is a layer, not a fix.

After the official patch

Update PHP to the patched release and remove these rules where you can. Keep them only for branches that will not be patched (EOL 8.1 and earlier), and re-check against PHP's advisory.

Until you patch, watch for signs that someone is probing:

PHP worker crashes (PHP-FPM or LiteSpeed's lsphp ) with SIGSEGV/SIGABRT in the logs, especially correlated with multipart POSTs.

Access-log multipart POSTs carrying a PHP_SESSION_UPLOAD_PROGRESS field to routes that never accept uploads.

Session files in session.save_path with upload-progress entries for sessions that never uploaded, or rewritten unusually often.

This repo accompanies the guide on pasqualepillitteri.it:

php2shell, block PHP attacks with Nginx and Apache before the patch

More related security coverage:

PHP RCE flaws leak, patch arrives October 21

Drupal patches two critical RCE flaws, update before the exploits

Nextcloud, PHP web shell via Collabora CODE

NGINX, five vulnerabilities fixed in 1.31.7 and 1.30.6

October 2026 Patch Tuesday, record 1,411 flaws and three zero-days

Extracted Entities