Repository navigation
Temporary BunkerWeb filters for PHP2Shell (CVE-2026-109618 and CVE-2026-109619) #4085
Replies: 1 comment
|
PHP published an informational advisory today that changes the removal advice above. The upload-progress path can still lead to code execution on the fixed releases when an application sets session.upload_progress.enabled = 0So keep The input-limits config isn't affected by this. |
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Update, 23 October: we missed the request shape the published exploit uses, so the input-limits config now ends with two more rules, 78183 and 78184. We also got the reporting wrong. BunkerWeb doesn't recognize the custom
tx.bunkerweb_reasonvalues we used. With the default deny status, our 403 blocks normally showed up in Reports asunknown, without the rule ID, and detect-mode matches could be missing from Reports. Every deny rule now setstx.bunkerweb_reason=modsecurityand the variables that carry the rule ID and message. If you copied either config earlier, replace both with the versions below. Changing the reporting actions doesn't change what a rule matches or blocks.PHP shipped the fixes for both PHP2Shell vulnerabilities on 21 October: 8.5.12, 8.4.27, 8.3.36 and 8.2.35, with source backports for 7.4, 8.0 and 8.1. Updating PHP is the fix, and PHP's own advisory calls it the only safe choice. If you can't patch every PHP worker today, the two custom configs below cut down what reaches PHP through BunkerWeb in the meantime. They don't make an unpatched PHP safe, and they do nothing for traffic that reaches your origin without going through BunkerWeb.
The first one covers CVE-2026-109619. PHP confirmed the session upload-progress field as the entry point, so the config rejects
PHP_SESSION_UPLOAD_PROGRESSin parsed field names, including the dot, space and array forms PHP turns into the same key. It also checks the raw multipart part headers, in case ModSecurity and PHP read a name differently. It doesn't cover the other stale-state paths in the advisory.The second one covers CVE-2026-109618 the way PHP's advisory suggests for proxies: it caps the number of fields and multipart parts, and the JSON nesting depth. It also caps field names nested 8 levels or deeper at 100: PHP builds the whole name hierarchy up to six times for every nested multipart control, empty file controls included, and the exploit sends many of them. With the field and part budgets alone, anything from 51 to about 4,999 of them got through. The deny rules don't rely on CRS anomaly scoring, so they apply with CRS v4, CRS v3 or CRS off.
SecArgumentsLimithas to stay above the deny threshold. Past its limit ModSecurity stops counting without raising an error: set to 2000, it saw 2000 of 3000 fields and PHP still received all 3000. We started at 1000 fields, and that blocked saving a 300-item WordPress menu (3608 fields), hence 5000. A smaller app can go lower withSecArgumentsLimit 2000and both@gt 5000changed to@gt 1000. Real forms have a handful of names nested that deep. If one of your forms has more than 100, run detect mode first and look for 78184 in the logs.Add both as
modseccustom configs, globally or on your PHP services, from the web UI or on the scheduler:They need BunkerWeb 1.6.0 or later. On the PHP services, keep
USE_MODSECURITY=yes,MODSECURITY_SEC_RULE_ENGINE=OnandSECURITY_MODE=block, because in detect mode these rules only log. KeepMODSECURITY_SEC_REQUEST_BODY_LIMIT_ACTION=Rejecttoo, becauseProcessPartiallets the end of a large body through uninspected. SetMAX_CLIENT_SIZEto8mwithout uploads or32mwith uploads, never0. That limit applies to chunked bodies too, but a location'sREVERSE_PROXY_MAX_CLIENT_SIZE, numbered instances included, can raise it or set it to0, so check those. KeepREVERSE_PROXY_MODSECURITY=yeson every PHP location, numbered instances included, so these rules run there. With CRS enabled, whitelisted clients skip ModSecurity; with CRS off, these rules still run for them. 1.6.17 already caps request headers at 100 withMAX_HEADERS, a setting 1.6.0 doesn't have.If you changed
session.upload_progress.name, put your name in both patterns in place ofphp[ ._]session[ ._]upload[ ._]progress, in lowercase, with each_written as[ ._]and any other regex metacharacter escaped, and test the rules before you deploy them. On the PHP side,session.upload_progress.enabled=0in the startup config, followed by a restart of every worker, is the stronger stopgap for CVE-2026-109619.We tested both on BunkerWeb
1.6.17~rc2with CRS v4, CRS v3 and CRS off, in front of PHP 8.4. That covered 26 multipart and encoding variants of the field (none that PHP could see got through), the field, part, depth and size boundaries, and a real WordPress: install, login, Gutenberg saves, a media upload, the 300-item menu and comments. The upload-progress filter also reads raw part headers, so an upload whose filename contains the token gets a 403 as well. We tested the nested-name rules on BunkerWeb1.6.17with CRS v4: 100 nested names pass and 101 get a 403, whether they're empty file controls, plain fields, file parts or a URL-encoded body. Nothing in the WordPress set above tripped them, and neither did uploads of 1 to 51 files, Nova forms nested 5 levels, single forms nested 11 and 12 levels, or JSON 30 levels deep.To check the upload-progress filter, from an IP that isn't whitelisted:
Then look for
78161in the BunkerWeb logs or in Reports, because another protection can also return 403. This doesn't test the input-limits config.Keep the upload-progress config until the next regular PHP release runs on every PHP worker, even after the 21 October fixes, because of CVE-2026-115560. The input limits are reasonable hardening to keep if they don't get in your way.
All reactions