<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom"><title>Peakhour.IO - Vulnerability Management</title><link href="https://www.peakhour.io/" rel="alternate"></link><link href="https://www.peakhour.io/feeds/tag/vulnerability-management.atom.xml" rel="self"></link><id>https://www.peakhour.io/</id><updated>2026-10-22T12:28:31+11:00</updated><entry><title>PHP2Shell probes and PHP multipart upload vulnerabilities</title><link href="https://www.peakhour.io/blog/php2shell-upload-probes/" rel="alternate"></link><published>2026-10-22T00:00:00+11:00</published><updated>2026-10-22T12:28:31+11:00</updated><author><name>Dan</name></author><id>tag:www.peakhour.io,2026-10-22:/blog/php2shell-upload-probes/</id><summary type="html">&lt;p&gt;How PHP2Shell probes target CVE-2026-109618 and CVE-2026-109619, what we captured, and how WAF protections help block these requests.&lt;/p&gt;</summary><content type="html">&lt;p&gt;PHP2Shell has sent website operators around the world scrambling to patch. Government portals and hospital websites across Europe have been taken offline as a precaution, and the Wikimedia Foundation has disabled some supporting systems until they can be safely updated. &lt;a href="https://www.reuters.com/technology/websites-taken-offline-over-php-risks-2026-10-21/"&gt;Reuters has a rundown of the affected services&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The concern centres on two critical vulnerabilities in PHP, the software behind many websites and content management systems. Disclosed on 21 October 2026, &lt;a href="https://github.com/php/php-src/security/advisories/GHSA-w8hw-w9fx-j79w"&gt;CVE-2026-109618&lt;/a&gt; and &lt;a href="https://github.com/php/php-src/security/advisories/GHSA-6wmf-4rv5-p3rp"&gt;CVE-2026-109619&lt;/a&gt; can allow an attacker to run code on a vulnerable server without logging in. PHP has released fixes, leaving operators with the urgent job of finding affected systems and getting them updated.&lt;/p&gt;
&lt;p&gt;At Peakhour, we were also seeing suspicious traffic. Early on 22 October, one of our honeypots captured a request that combined upload-progress tracking with an unusual collection of empty file uploads. Its structure matched a PHP2Shell-style probe. We did not observe code execution.&lt;/p&gt;
&lt;p&gt;If you run PHP, the priority is to install a fixed release and restart the PHP workers that serve your website. A web application firewall (WAF) can block known request patterns while you roll out the update. Peakhour's WAF blocks requests containing PHP's default upload-progress field, and we are updating protections as new patterns emerge.&lt;/p&gt;
&lt;h2&gt;What we saw at the honeypot&lt;/h2&gt;
&lt;p&gt;At 03:15:36 AEDT on 22 October 2026, a client at &lt;code&gt;94.154.43.7&lt;/code&gt; sent a GET request to one of our honeypots. About 1.4 seconds later, it sent a multipart POST to &lt;code&gt;/&lt;/code&gt; on port 8080.&lt;/p&gt;
&lt;p&gt;Multipart forms are the format browsers commonly use to send files alongside ordinary form fields. This request contained 151 file entries with empty filenames. Their field names were nested 60 bracket levels deep. It also included &lt;code&gt;PHP_SESSION_UPLOAD_PROGRESS&lt;/code&gt; and &lt;code&gt;PHPSESSID&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Those details are what made the request stand out. Even an upload with no filename can make PHP allocate memory, and deeply nested field names add more work. The upload-progress field brings session handling into the process while PHP is still reading the body.&lt;/p&gt;
&lt;p&gt;Our WAF logs showed upload-progress probes that morning too. At 08:49 AEDT, requests from &lt;code&gt;169.58.51.5&lt;/code&gt; triggered blocks for the upload-progress field and forms with too many arguments.&lt;/p&gt;
&lt;p&gt;We checked the honeypot logs through 10:50 AEDT on 22 October and found no further requests combining upload-progress tracking with deeply nested, empty-filename file entries. The connection details and source addresses are at the end of this post for anyone checking their own logs.&lt;/p&gt;
&lt;h2&gt;Why an empty upload can still cause trouble&lt;/h2&gt;
&lt;p&gt;An empty filename might suggest that PHP has little to process. But PHP still has to represent the file entry internally. Three parts of this request matter.&lt;/p&gt;
&lt;p&gt;First, upload-progress tracking starts early. When the feature is enabled, a field named &lt;code&gt;PHP_SESSION_UPLOAD_PROGRESS&lt;/code&gt; tells PHP to track the upload and begin session processing as the request body arrives. That reaches PHP internals before the application's normal request handling begins.&lt;/p&gt;
&lt;p&gt;Second, PHP's &lt;code&gt;max_file_uploads&lt;/code&gt; limit does not count entries with empty filenames. Those entries still create file metadata, so that setting alone does not bound the memory used by a request like this.&lt;/p&gt;
&lt;p&gt;Third, PHP turns bracketed field names into arrays. Nesting those names 60 levels deep makes PHP build more complex data structures. Repeating that across many file entries adds memory pressure, even when the entries contain no useful files.&lt;/p&gt;
&lt;p&gt;Together, these features give a crafted request a way to put pressure on PHP's memory handling. The vulnerabilities concern what PHP does when that processing goes wrong.&lt;/p&gt;
&lt;h2&gt;What the two vulnerabilities do&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://github.com/php/php-src/security/advisories/GHSA-w8hw-w9fx-j79w"&gt;CVE-2026-109618&lt;/a&gt; concerns out-of-memory handling. If PHP runs out of memory while preparing a request, it can leave internal data only partly initialised. When PHP cleans up the failed request, it may treat that incomplete data as valid. An attacker can exploit the resulting memory corruption to run code.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://github.com/php/php-src/security/advisories/GHSA-6wmf-4rv5-p3rp"&gt;CVE-2026-109619&lt;/a&gt; concerns state left behind between requests. A PHP worker is a process that handles web requests, often one after another. This bug can leave a worker holding a reference to memory that a previous request has already released. A later request handled by that same worker may use the old reference before PHP resets it. This is a use-after-free vulnerability, which can also lead to code execution.&lt;/p&gt;
&lt;p&gt;The advisories give the attacks high complexity ratings. Reliability depends on the PHP build, extensions, memory layout and how requests reach workers. For the second vulnerability, an attacker also needs a later request to reach the same worker and act on the leftover state.&lt;/p&gt;
&lt;p&gt;Those conditions make a reliable exploit harder to build. The PHP team has nevertheless demonstrated unauthenticated code execution against Apache &lt;code&gt;mod_php&lt;/code&gt;, before application code runs, including with multiple workers. &lt;a href="https://github.com/php/php-src/security/advisories/GHSA-w8hw-w9fx-j79w#impact-and-reliability"&gt;PHP's exploit assessment&lt;/a&gt; explains the reliability factors.&lt;/p&gt;
&lt;p&gt;Our assessment is that automated retries can reduce the practical burden of reaching the required conditions. Repeated multipart probes, memory failures and unexpected worker exits deserve investigation while teams patch. The request we captured is evidence of probing; it does not establish a successful compromise.&lt;/p&gt;
&lt;h2&gt;Update PHP and restart the workers&lt;/h2&gt;
&lt;p&gt;PHP released fixes on 21 October in versions &lt;strong&gt;8.2.35, 8.3.36, 8.4.27 and 8.5.12&lt;/strong&gt;. Install a fixed release for your branch, or a vendor package that includes the fixes, then restart every PHP worker serving web requests.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://www.php.net/archive/2026.php#2026-10-21-5"&gt;PHP's release notice&lt;/a&gt; identifies Apache &lt;code&gt;mod_php&lt;/code&gt; as the highest-risk setup and urges all installations serving web requests to update. Check the PHP runtime your website actually uses when verifying the rollout.&lt;/p&gt;
&lt;p&gt;A WAF helps during the gap between disclosure and patching. It can inspect incoming requests and block known attack patterns before they reach PHP. Rules can also be updated centrally as probes change, and the logs can show which requests need attention.&lt;/p&gt;
&lt;p&gt;That is the role of Peakhour's upload-progress protection during this rollout. Patching fixes the underlying PHP vulnerabilities; WAF rules reduce exposure to the request patterns they recognise.&lt;/p&gt;
&lt;h2&gt;Temporary PHP settings while you patch&lt;/h2&gt;
&lt;p&gt;The following measures reduce exposure to particular attack paths. Updating PHP remains the complete fix. Apply configuration changes to the PHP runtime serving the website, restart every worker and check the effective settings.&lt;/p&gt;
&lt;h3&gt;Disable session upload progress&lt;/h3&gt;
&lt;p&gt;For CVE-2026-109619, disable session upload progress in configuration that applies before PHP parses the request. An FPM pool can enforce this with:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="na"&gt;php_admin_flag[session.upload_progress.enabled]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;off&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The equivalent &lt;code&gt;php.ini&lt;/code&gt; setting is &lt;code&gt;session.upload_progress.enabled = Off&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Timing matters here. A script-level &lt;code&gt;ini_set()&lt;/code&gt; call and FPM &lt;code&gt;.user.ini&lt;/code&gt; take effect too late for this mitigation. Disabling upload progress blocks the confirmed session paths, but the advisory describes other ways to reach the vulnerability.&lt;/p&gt;
&lt;p&gt;Also review invalid or deprecated settings applied before request initialisation, and fix attacker-reachable fatal errors and execution timeouts. On PHP 8.5, &lt;code&gt;fatal_error_backtraces = Off&lt;/code&gt; blocks the confirmed fatal-backtrace path. That setting is specific to PHP 8.5.&lt;/p&gt;
&lt;h3&gt;Handle memory limits with care&lt;/h3&gt;
&lt;p&gt;For CVE-2026-109618, &lt;a href="https://github.com/php/php-src/security/advisories/GHSA-w8hw-w9fx-j79w"&gt;PHP's advisory&lt;/a&gt; recommends temporarily enforcing &lt;code&gt;memory_limit = -1&lt;/code&gt; administratively. This removes the predictable failure caused by PHP's own memory limit. It also removes PHP's per-request memory bound, so plan external worker or container memory controls before making the change.&lt;/p&gt;
&lt;p&gt;An FPM pool can enforce the setting with:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="na"&gt;php_admin_value[memory_limit]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;-1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Check that pool, directory and application settings do not restore a finite limit. Prefer external controls that can terminate an affected worker without taking down the whole pool; a shared cgroup limit can affect every worker in it. Underlying allocation failures can still trigger the bug.&lt;/p&gt;
&lt;p&gt;For unpatched workers, avoid lowering &lt;code&gt;memory_limit&lt;/code&gt; or substituting &lt;code&gt;RLIMIT_AS&lt;/code&gt; or &lt;code&gt;RLIMIT_DATA&lt;/code&gt;. These can cause the allocation failures that reach the vulnerable code.&lt;/p&gt;
&lt;h3&gt;Limit the work an upload can create&lt;/h3&gt;
&lt;p&gt;Keep &lt;code&gt;post_max_size&lt;/code&gt; conservative. PHP recommends &lt;code&gt;8M&lt;/code&gt; for applications without uploads and &lt;code&gt;32M&lt;/code&gt; where uploads are needed. Check those values against your application's normal requests before applying them, and keep &lt;code&gt;upload_max_filesize&lt;/code&gt; below &lt;code&gt;post_max_size&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;At the web server or proxy, cap decoded request-body size. Make sure the limit also covers chunked requests and requests without a usable &lt;code&gt;Content-Length&lt;/code&gt;. Bound input variables, multipart parts, part headers and nesting depth as well. Remember that &lt;code&gt;max_file_uploads&lt;/code&gt; does not count empty-filename entries, even though PHP still allocates metadata for them.&lt;/p&gt;
&lt;p&gt;The &lt;a href="https://www.php.net/archive/2026.php#2026-10-21-5"&gt;official PHP guidance&lt;/a&gt; explains these temporary measures and their limits.&lt;/p&gt;
&lt;h2&gt;What to look for in your logs&lt;/h2&gt;
&lt;p&gt;Start by comparing unusual multipart requests with the uploads your application normally receives. Look for combinations of:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Many file entries with empty filenames.&lt;/li&gt;
&lt;li&gt;Deeply nested field names.&lt;/li&gt;
&lt;li&gt;Unusually large numbers of multipart parts or part headers.&lt;/li&gt;
&lt;li&gt;Upload-progress fields, especially alongside the patterns above.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Review nearby requests too, including a GET just before an upload, as we saw at the honeypot. Check PHP and web server logs for memory failures and unexpected worker exits around the same time.&lt;/p&gt;
&lt;p&gt;If you block the upload-progress field, check its configured name. &lt;code&gt;PHP_SESSION_UPLOAD_PROGRESS&lt;/code&gt; is the default, but administrators can change it with &lt;code&gt;session.upload_progress.name&lt;/code&gt;. &lt;a href="https://www.php.net/manual/en/session.configuration.php#ini.session.upload-progress.name"&gt;PHP documents that setting here&lt;/a&gt;. A rule matching only the default name will miss a renamed field.&lt;/p&gt;
&lt;p&gt;Also check what happens when a request is too large or malformed for your WAF to inspect fully. Monitor parsing errors and tune limits around legitimate application traffic. Our guide to &lt;a href="https://www.peakhour.io/blog/how-to-tune-your-waf/"&gt;WAF tuning&lt;/a&gt; covers this process.&lt;/p&gt;
&lt;p&gt;The captured request gives us a useful pattern to investigate. Future probes could change the multipart layout or use other PHP processing paths, so review new patterns as they appear. Keep the PHP update as the priority, then use request limits, WAF rules and log review to support it.&lt;/p&gt;
&lt;h2&gt;Connection fingerprints for log searches&lt;/h2&gt;
&lt;p&gt;Connection fingerprints summarise characteristics of a client's requests or connections. They can help you find similar traffic across different source addresses. Treat a shared fingerprint as a clue about a group of clients, rather than proof that requests came from one client. Our guide explains &lt;a href="https://www.peakhour.io/blog/fingerprint-is-a-cohort-not-a-client/"&gt;how to interpret fingerprint groups&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;We calculated the following &lt;a href="https://www.peakhour.io/blog/overview-of-ja4-network-fingerprinting/"&gt;JA4H HTTP fingerprints&lt;/a&gt; from saved headers using &lt;a href="https://github.com/FoxIO-LLC/ja4/blob/main/python/ja4h.py"&gt;FoxIO's reference implementation&lt;/a&gt;. The TLS values are those recorded in each WAF probe group. The honeypot request used plaintext HTTP, so it has no TLS fingerprint.&lt;/p&gt;
&lt;h3&gt;WAF probe group one&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;HTTP JA4H  po11nn060000_84bafeeaa541_000000000000_000000000000
Observed TLS JA4  t13d131100_f57a46bbacb6_ab7e3b40a677
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h3&gt;WAF probe group two&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;HTTP JA4H  po11nn060000_4ea4093e6290_000000000000_000000000000
Observed TLS JA4  t13d131200_f57a46bbacb6_9249cab70c77
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h3&gt;WAF probe group three&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;HTTP JA4H  po11nn060000_5c15dc981d69_000000000000_000000000000
Observed TLS JA4  t13d911100_0d5420ba6086_ec21bdc2dd35
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h3&gt;WAF probe group four&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;HTTP JA4H  po11nn050000_197af124696f_000000000000_000000000000
Observed TLS JA4  t13d171200_ab0a1bf427ad_ecd0401ec68b
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h3&gt;Honeypot request&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;HTTP JA4H  po11nn060000_d09ed10a732b_000000000000_000000000000
TLS JA4    not applicable; plaintext HTTP
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The honeypot request declared this browser user-agent:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0 Safari/537.36
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Its TCP signature was &lt;code&gt;1460|7|1|1|64240|0x02&lt;/code&gt;. Two WAF probes, from &lt;code&gt;160.187.210.214&lt;/code&gt; and &lt;code&gt;169.58.51.5&lt;/code&gt;, shared those TCP settings but had different HTTP fingerprints.&lt;/p&gt;
&lt;p&gt;Several other WAF records shared the same declared body size, headers and browser string. One used &lt;code&gt;Mozilla/5.0 (php2shell-check/1.0)&lt;/code&gt;. These details provide more ways to search logs. Our guide to &lt;a href="https://www.peakhour.io/blog/recognising-vulnerability-scanners/"&gt;recognising vulnerability scanners&lt;/a&gt; shows how to combine these clues across source addresses.&lt;/p&gt;
&lt;h2&gt;Observed source addresses&lt;/h2&gt;
&lt;p&gt;These addresses appeared in upload-progress probes between 00:00 AEDT on 21 October and 09:00 AEDT on 22 October 2026, or in the honeypot request at 03:15 AEDT on 22 October. Use them to search your logs and review the associated requests before deciding whether to block an address.&lt;/p&gt;
&lt;p&gt;```text
2.27.152.153  13.143.120.218  23.27.75.120  23.27.203.212
23.236.222.100  24.144.109.162  31.56.137.245  31.56.138.52
31.58.9.242  31.58.20.52  31.58.148.208  31.59.27.195
34.87.183.75  38.154.233.215  43.229.11.209  45.38.92.232
45.38.114.219  45.39.7.39  45.39.7.227  45.39.27.153
45.39.123.39  45.41.171.199  45.41.176.106  45.41.177.11
45.43.67.111  45.43.82.93  45.61.98.240  45.61.100.180
45.61.123.134  45.67.0.207  45.67.3.53  45.147.187.214
45.151.163.80  45.159.53.193  45.249.59.5  45.249.59.123
46.202.248.133  46.203.154.134  46.203.202.8  46.203.212.201
62.105.202.173  62.105.203.138  64.64.118.134  64.137.8.237
64.137.73.58  64.137.100.131  64.137.101.226  64.137.108.41
67.227.1.14  67.227.37.59  68.167.47.41  68.183.138.186
82.21.221.119  82.21.248.189  82.22.215.207  82.22.234.160
82.22.249.84  82.23.227.154  82.24.212.115  82.25.235.118
82.27.247.49  82.29.233.5  82.29.245.108  89.45.125.116
89.249.192.235  91.211.87.239  92.112.91.92  92.112.143.195
92.112.170.208  92.113.119.3  92.113.236.71  92.113.246.14
94.154.43.7  94.177.5.146  102.212.90.53  103.47.53.192
103.99.34.177  103.101.90.134  103.105.76.121  104.168.118.192
104.252.49.100  104.253.50.139  104.253.81.55  104.253.91.46
107.172.163.190  107.174.194.247  108.165.205.76  136.0.108.192
136.0.184.80  140.99.189.141  140.99.193.124  140.99.203.75
142.111.44.12  142.111.216.8  142.147.129.75  142.147.132.10
145.223.41.159  145.223.45.93  145.223.50.100  148.135.254.33
148.135.254.73  148.135.254.118  151.245.204.165  151.245.204.214
151.245.205.41  151.245.206.59  152.232.81.206  154.9.177.66
154.29.25.85  155.254.39.35  160.187.210.214  166.88.34.50
166.88.64.163  168.199.209.224  168.199.244.14  169.58.51.5
172.120.112.207  172.120.119.224  173.214.177.173  185.101.252.60
192.95.91.217  192.177.87.44  192.186.186.222  198.12.112.116
198.20.191.165  198.46.148.181  198.46.202.184  198.46.241.29
198.89.123.223  198.105.100.102  208.70.8.237  212.42.207.214
213.169.211.115  216.10.27.39  217.117.165.104&lt;/p&gt;</content><category term="Security"></category><category term="PHP"></category><category term="WAF"></category><category term="Application Security"></category><category term="Threat Detection"></category><category term="Vulnerability Management"></category></entry></feed>