Skip to main content
osky
Sección
Infrastructure

PHP2Shell is no longer a theoretical advisory. On October 26, 2026, the Dream research team published artifacts from a campaign that chained the two critical PHP vulnerabilities patched on October 21 (CVE-2026-109618 and CVE-2026-109619) with Fragnesia, a Linux kernel flaw, to get root on 25 servers. Three of them belong to an environment Dream describes as affiliated with the US government. It all started with a single HTTPS request, no credentials, against a firewall administration interface exposed to the internet. If you run servers with PHP or devices with a web panel, this is the practical version of why you don't put off a patch.

What Dream found

According to the report, on October 25 the researchers found the operation's complete kit: a custom PHP2Shell exploit (not a copy of the public ones), the Fragnesia exploit (CVE-2026-46300), target lists, exploitation logs and about 40 post-exploitation scripts. The compromised machines run specific versions of firewall software from "a large vendor." For safety, Dream does not reveal the brand or the affected versions, and says it has already notified the vendor.

The operating system on those firewalls uses PHP 8.0.30, a branch the PHP project no longer maintains, with a high memory limit that, according to Dream, makes the memory corruption stages easier. The admin web server handles unauthenticated requests, and that's where the attack gets in.

How the chain works

Dream describes four stages inside the PHP exploit:

  1. Exhaust memory. The attacker sends multipart requests with huge file names and session progress data (session.upload_progress) until PHP reaches about 383 MB and a predictable memory layout is forced.
  2. Leak an address. A second request triggers an out-of-bounds read, and the Set-Cookie header in the response returns a pointer with the base address of libphp.so.
  3. Leave a dangling reference. Another round of memory pressure (about 500 MB) leaves a reference to memory that has already been freed.
  4. Execute. The final request forges a hash table entry in that memory whose destructor points to PHP's system() function. When PHP cleans up the request, it runs the attacker's command as the nobody user.

That wasn't enough: SELinux prevented that process from modifying system files. That's why the second link is Fragnesia, a local privilege escalation in the kernel's XFRM ESP-in-TCP subsystem. According to Dream, the exploit writes to the page cache to replace the rules in /etc/pam.d/su with pam_permit.so, which accepts anything. After that, su root gets in without a password. From the initial connection to a root webshell takes less than 15 minutes.

Diagrama de la cadena PHP2Shell más Fragnesia y del embudo de la campaña, de 19,000 firewalls expuestos a 25 servidores comprometidos

From mass scanning to Active Directory

The campaign followed a very clear funnel, again according to the report:

Stage Count
Exposed vendor firewalls, found with the Chinese search engine FOFA ~19,000
Responded with their version ~5,000
Filtered to the US, Japan, Taiwan and South Korea and to versions with an exploit profile 240
Firewalls rooted after the automated batch 24
Internal management server compromised afterward 1 (25 in total)

The exploit needs different memory addresses for each version of the firewall's operating system. Dream found 11 version profiles in JSON, extracted from the libphp.so of each one, and notes that the attacker had profiles for nine exploitable versions.

On 22 of the 24 firewalls the attacker only dropped a 103-byte webshell and left. Dream calls them "owned and parked": compromised and parked to come back whenever they want. The other two belonged to the same organization. The configuration extracted from the first one showed CAC card authentication, government-style hostnames and the complete internal topology: domain controllers, the central firewall management server, Keycloak, GitLab and VPN. That first firewall went down shortly after, probably because of the ~900 MB of exploit traffic. The second went down on October 24 at 10:09 UTC.

From there, according to the report, the attacker set up an eight-port reverse SSH tunnel that survives reboots, used that tunnel to launch the same chain against the internal server that manages all of the organization's firewalls (11:35 UTC, less than 90 minutes later) and from there attacked Active Directory. They cracked the password of the LDAP service account stored in the configuration, enumerated users and groups, and ran password spraying against every account. The domain had lockoutThreshold=0, meaning no lockout after failed attempts. They also tried to create user and machine accounts as a backdoor and to extract Keycloak's SAML signing key. Dream couldn't confirm whether these last attacks worked, because the results stayed on the compromised server.

Attribution and the AI in the kit

Dream does not attribute the operation to a specific group, but it lists signs pointing to a China-linked origin: the four target countries, the use of FOFA and the iox tunneling tool, clocks set to UTC+8, and Chinese-language messages in the logs of a pentesting platform called aiscan configured with DeepSeek and Kimi. Claude Code was also installed on the infrastructure. Dream makes clear it has no direct evidence that AI wrote the exploit. It suggests it based on the structure and consistency of the ~40 scripts, and says it doesn't look like a fully autonomous attack.

My take: the AI detail is what will make the most noise, but it's not what took anyone down. What took these organizations down was an admin panel open to the internet, running unsupported PHP, and a domain with no account lockout. Those are hygiene failures from twenty years ago.

What PHP says and what to do

On October 21, the PHP project released PHP 8.5.12, 8.4.27, 8.3.36 and 8.2.35 and urged everyone to update immediately. Its advisory says "all PHP installations serving web requests" are at risk, including PHP-FPM, Apache with mod_php, php-cgi, FrankenPHP and LiteSpeed, and that the risk is higher with mod_php. The CVE-2026-109618 advisory clarifies that with LSAPI (LiteSpeed), application-dependent exploits are "likely but not validated." There are no official releases for PHP 8.1 and earlier, but the security team published minimal source-code backports for 7.4, 8.0 and 8.1. That's exactly the case of the firewalls running 8.0.30: it depends on the vendor.

If you can't update today, PHP proposes partial mitigations and makes it clear that they "do not make an installation safe":

  • CVE-2026-109618 (out-of-memory corruption): memory_limit=-1 enforced at the administrator level, a low post_max_size (8M if there are no uploads, 32M if there are) and limits on request size, fields and multipart parts on the web server or proxy.
  • CVE-2026-109619 (stale global state): session.upload_progress.enabled=0 in php.ini or in the pool configuration, restarting all workers. Doing it with ini_set() or .user.ini doesn't work. In PHP 8.5, also fatal_error_backtraces=0.

On October 23, PHP added an informational advisory about a third flaw, CVE-2026-115560, which only applies if the application changes session.serialize_handler to php at runtime with upload progress enabled. It will fix it in the next regular release and recommends disabling upload progress in the meantime.

About Fragnesia: it was disclosed in May with a public proof of concept. Ubuntu gives it high priority ("trivial local privilege escalation") and has published fixed kernels since June. Red Hat rates it Important because it requires local access. This campaign shows why "requires local access" reassures no one: PHP handed over the local access. If you can't reboot into a new kernel, Ubuntu points to the same mitigation as Dirty Frag, which is blocking the esp4, esp6 and rxrpc modules.

The short list for anyone running servers:

  1. Confirm the PHP version of every SAPI (CLI, FPM, LSAPI), not just the one in your terminal, and restart the workers after updating.
  2. Update the kernel and actually reboot. A patch installed without a reboot doesn't protect you.
  3. Take the admin interfaces of firewalls, NAS devices, routers and panels off the internet: VPN, IP allowlist or private network.
  4. Ask your appliance vendor which PHP version their firmware ships and when the patch is coming.
  5. Check the account lockout threshold and MachineAccountQuota in Active Directory.

Sources