PHP2Shell + Fragnesia: The Chain That Turns a PHP Bug Into Root (and Why Django Teams Should Care)

On October 29, 2026, CISA added a seven-month-old Linux kernel bug to its Known Exploited Vulnerabilities (KEV) catalog with an unusual note attached. The bug is CVE-2026-46300, nicknamed Fragnesia, and CISA's entry says it "can be chained with CVE-2026-109618 and CVE-2026-109619 to implant PHP webshells and run commands as root" (BleepingComputer). Those two CVEs are PHP2Shell, the PHP remote code execution pair disclosed on October 21.
The same day, CISA also added CVE-2026-97229, a CVSS 10.0 remote code execution bug in self-managed GitLab whose root cause sits in Ruby's regular expression engine (CISA).
If your stack is Django, FastAPI or Node, it's tempting to scroll past a PHP story and a GitLab story. We think that would be a mistake, and this post explains why. The short version: the attack that made this week's headlines didn't need a PHP app you wrote. It hit the PHP built into a firewall's management page, then used a kernel bug that exists on ordinary Linux servers to turn "code running as nobody" into "root". That second step works the same way under any web stack.
Below is what is confirmed, who has to act, the fixed versions, commands to check your machines, the temporary mitigations, and our take. Facts and opinion are kept apart. The "What we know" sections come from the advisories and research linked inline. The "Our take" sections are our opinion and are labelled that way.
TL;DR
- PHP2Shell (CVE-2026-109618, CVE-2026-109619): two critical (CVSS 9.2) memory-safety bugs in PHP that allow unauthenticated remote code execution. Every PHP version from 5.4 through 8.5.11 is affected. Fixed in PHP 8.5.12, 8.4.27, 8.3.36 and 8.2.35. PHP 8.1 and older get no upstream fix (Google Cloud, CISA). In KEV since October 26.
- Fragnesia (CVE-2026-46300): a Linux kernel local privilege escalation in the XFRM ESP-in-TCP path that lets an unprivileged process write into the page cache of read-only files and become root. Fixed in kernel 5.10.257, 5.15.208, 6.1.174, 6.6.141, 6.12.91, 6.18.33 and 7.0.10, and in distro kernels since mid-May (kernel CVE announcement). In KEV since October 29, with a federal deadline of October 31 (Help Net Security).
- The chain in the wild: Dream Research recovered tooling from a campaign that used both bugs in a single HTTP request to root 25 Palo Alto Networks PAN-OS firewall and management servers, then pivoted into a US government-affiliated Active Directory (Dream Research).
- GitLab CVE-2026-97229: unauthenticated RCE through a crafted regex in CI/CD configuration, CVSS 10.0. Fixed in GitLab 19.4.2, 19.3.5 and 19.2.8, but if you build GitLab on your own Ruby you also need Ruby 3.3.13, 3.4.12 or 4.0.9 (GitLab, Ruby). Federal deadline November 1.
- Who must act today: anyone running PHP anywhere (including appliances and that WordPress marketing site), anyone running Linux hosts that execute code they don't fully trust (container hosts, CI runners, notebook servers, shared boxes), and anyone self-hosting GitLab.
What we know: PHP2Shell
PHP2Shell was disclosed on October 21, when the PHP project shipped fixes at 13:30 UTC (CERT-EU). The research team that found it, Calif, says the earliest affected path dates to PHP 5.4.0 and that the bugs are "always exploitable under default configurations", with the available exploitation path depending on the version and the server API (Calif Research).
There are two bugs, both rated 9.2:
- CVE-2026-109618 (GHSA-w8hw-w9fx-j79w) is memory corruption during out-of-memory handling. PHP enforces
memory_limitby usinglongjmp()to abort a request that goes over it. That abort can land while internal request data is only partly initialised, for example while parsing a multipart file upload. Request cleanup then treats the half-built data as valid and frees memory that was never set up, memory that may still hold attacker-controlled bytes from earlier allocations (CERT-EU). - CVE-2026-109619 (GHSA-6wmf-4rv5-p3rp) is stale global state. Internal state owned by one request can outlive its backing memory, for example after a fatal error, and a later request handled by the same worker can use it before normal initialisation resets it. PHP's advisory lists memory disclosure as a confirmed impact of this one too (Tenable).
Every PHP installation that serves web requests is in scope: PHP-FPM, Apache with mod_php, php-cgi, FrankenPHP and LiteSpeed. The PHP team considers mod_php the highest-risk setup because a demonstrated exploit triggers the bug with one unauthenticated request before any application code runs (CISA, Canadian Centre for Cyber Security).
That last point matters. "Our PHP app is tiny and has no upload form" is not a defence. The vulnerable code is in PHP's own request handling.
Exploitation is confirmed. CISA added both CVEs to KEV on October 26. Perplexity security engineer Adel Karimi reported that honeypots recorded remote code execution against an Apache mod_php install running PHP 8.4.26 over the weekend before that (BleepingComputer). CISA's guidance is blunt: organisations that can't update, or that still run PHP 8.1 or earlier, "should take PHP-based systems offline until they can be patched or updated" (CISA).
What we know: Fragnesia
Fragnesia isn't new. It was published in May 2026 as part of the "Dirty Frag" family of kernel networking bugs, and distro patches shipped around May 13 (AlmaLinux, Red Hat RHSB-2026-003).
The mechanism, per AlmaLinux's write-up: socket-buffer code failed to carry the SKBFL_SHARED_FRAG marker across when moving paged fragments between buffers. The kernel then lost track of the fact that a fragment was backed by something external, such as page-cache pages spliced in from a file. The XFRM ESP-in-TCP receive path would then do in-place AES-GCM decryption directly over those page-cache pages. In plain terms, an unprivileged process can XOR chosen bytes into the cached copy of a read-only file like /usr/bin/su and use that to become root (AlmaLinux). The upstream fix touches __pskb_copy_fclone() and skb_shift() (kernel CVE announcement).
The bug was introduced in kernel 3.9, so it's old. The kernel CVE team lists these fixed stable versions: 5.10.257, 5.15.208, 6.1.174, 6.6.141, 6.12.91, 6.18.33 and 7.0.10. A public proof of concept exists and works against major distributions with kernels released before May 13, 2026 (BleepingComputer).
Some distro package versions that contain the fix:
| Distro | Fixed kernel package |
|---|---|
| Ubuntu 22.04 LTS | 5.15.0-181.191, or 6.8.0-124.124~22.04.1 (HWE) |
| Ubuntu 24.04 LTS | 6.8.0-124.124, or 6.17.0-35.35~24.04.1 (HWE) |
| Ubuntu 26.04 LTS | 7.0.0-22.22 |
| AlmaLinux 8 | kernel-4.18.0-553.124.3.el8_10 |
| AlmaLinux 9 | kernel-5.14.0-611.54.5.el9_7 |
| AlmaLinux 10 | kernel-6.12.0-124.56.3.el10_1 |
Sources: Ubuntu, AlmaLinux. Check your own distro's tracker for anything not listed here. Ubuntu notes that 18.04 and older, and 20.04 on its 5.4 kernel, are not affected.
Because it's a local privilege escalation, Fragnesia on its own needs the attacker to already be running code on the box. That is exactly what PHP2Shell gives them, and exactly what any remote code execution bug in any language gives them. CISA added Fragnesia to KEV on October 29 with the PHP2Shell chaining note and a forensic triage requirement, meaning US federal agencies have to check for compromise, not just patch (Help Net Security).
What we know: the chain Dream Research found
Dream Research published its findings on October 26 and updated them on October 29 after working with Palo Alto Networks. Everything in this section is from Dream's report unless noted.
- The target: the PAN-OS management web server, which also fronts the GlobalProtect portal. Dream says PAN-OS 12.1 and 12.2 use PHP 8.0.30, which is past end of life upstream, with a high memory limit that helped the memory-corruption stages. Palo Alto Networks' Unit 42 independently verified the tooling and tracks the activity as CL-UNK-1280, which it attributes to a Chinese-speaking attacker in the UTC+8 time zone. Dream itself does not attribute the campaign to a named group.
- The exploit: a custom PHP2Shell build, not a copy of a public PoC, in four stages. Two rounds of crafted multipart POSTs exhaust memory (roughly 383 MB, then roughly 500 MB). A leaked pointer comes back in a
Set-Cookieheader and gives away the base address oflibphp.so. The final request forges a PHP hash table entry whose destructor points at PHP'ssystem()function, so cleanup runs the attacker's command asnobody. - The escalation: SELinux stopped the web server user from modifying system files, so the attacker used Fragnesia to overwrite
/etc/pam.d/suwith a rule usingpam_permit.so. After that,su rootsucceeds with no password. Dream says both exploits run inside one HTTP request and the whole chain, from an unauthenticated HTTPS connection to a persistent root webshell, finishes in under 15 minutes. - The funnel: about 19,000 internet-facing PAN-OS devices found through the FOFA search engine, about 5,000 that responded with a version, 240 that matched the attacker's exploit profiles and target countries (US, Japan, Taiwan, South Korea), and 24 rooted in the automated batch. A third government server, the internal firewall management server, was rooted through a tunnel, which brings the total to 25.
- After root: a 103-byte PHP webshell at
/unauth/php/change_password.php, an 8-port reverse SSH tunnel kept alive through a systemd service and anrc.localhook, LDAP service-account credentials decrypted from the device config, full Active Directory user and group enumeration, and attempted password spraying (the domain hadlockoutThreshold=0), backdoor accounts and SAML signing-key theft from Keycloak. Dream says it could not tell whether those last attempts succeeded. - AI in the toolkit: Dream found Claude Code installed on the attacker's infrastructure and an offensive platform called aiscan configured with DeepSeek and Kimi models. It says it has no direct evidence the AI tools wrote the exploit, and that the operation "does not appear to be a fully autonomous nation-state attack."
Palo Alto Networks has a dedicated advisory covering which products are affected and the fixed PAN-OS and Panorama hotfixes: CVE-2026-109618 on Palo Alto Networks products. If you run PAN-OS, go there first, not to this post.
What we know: GitLab CVE-2026-97229 and the Ruby angle
The second KEV addition on October 29 is a different bug with a similar lesson about where the vulnerable code actually lives.
GitLab's October 12 patch release describes CVE-2026-97229 as an integer overflow in the CI/CD regular expression compiler that "could have allowed an unauthenticated user to execute arbitrary code on the GitLab server via a specially crafted regular expression in a CI/CD configuration." Affected: 19.2 before 19.2.8, 19.3 before 19.3.5 and 19.4 before 19.4.2, with a CVSS of 10.0. GitLab.com and GitLab Dedicated were already patched (GitLab).
The catch is that GitLab's fix is a patch to Ruby that GitLab applies in its own packages. Ruby's October 23 post explains that GitLab compiles user-supplied patterns with Regexp.new, and crafted patterns could corrupt memory inside Onigmo, Ruby's regex engine, while compiling. Ruby 4.0.9, 3.4.12 and 3.3.13 contain all the underlying fixes. Ruby also says that Regexp.timeout does not protect against these bugs, and that limiting pattern length doesn't reliably help because some are triggered by short patterns (Ruby).
Alpine Linux found this out the hard way. Its GitLab instance ran 19.3.4 on standard Ruby, saw an attempted exploit, upgraded to 19.3.6, and was still exposed until Ruby 3.3.13 landed. Alpine's alpinelinux/gitlab image was rebuilt on October 27. Anything pulled before that date should be pulled again (LavX News summary of Alpine's disclosure).
Ruby is clear about scope: the bugs are reached only when an application compiles a regex from attacker-controlled input, through Regexp.new, Regexp.union, Regexp.compile or interpolation like /#{input}/. Apps that only use regexes their own developers wrote are not affected.
GitLab published six detections for exploitation attempts, including Onigmo native worker crashes, hostile regexes sent to the GraphQL CI lint endpoint, and CI lint Workhorse worker crashes. Because CISA marked forensic triage as required, agencies have to review instances that sat unpatched after October 12, not just upgrade them (ctxsec summary).
Who has to act
Work down this list. If any line describes you, you have something to do today.
- You run PHP anywhere that serves web traffic. That includes the WordPress or Laravel marketing site next to your Python product, phpMyAdmin or Adminer on an ops box, a legacy admin panel, a PHP-based monitoring tool, and network appliances whose web UI is built on PHP. PHP is often bundled inside other software, so you may not see it directly (CISA).
- You run Linux hosts that execute code you don't fully control. EGI's advisory names grid worker nodes, container hosts, notebook servers and CI runners as needing urgent action for this bug class (EGI SVG). A Django server is on this list too in practice: if any dependency ever gives an attacker code execution as your app user, an unpatched kernel turns that into root.
- You self-host GitLab, especially from source, a custom image, or a distro package that uses the system Ruby.
- You run Ruby apps that compile regexes from user input. Search features, rule builders, "custom pattern" fields and filters often do this.
- You run PAN-OS or any appliance whose management interface is reachable from the internet.
Mitigation: PHP
The fix is to upgrade. Find what you have first.
# Version on the host
php -v
# PHP inside running containers
for c in $(docker ps -q); do
printf '%s ' "$(docker inspect -f '{{.Name}} {{.Config.Image}}' "$c")"
docker exec "$c" sh -c 'php -r "echo PHP_VERSION, PHP_EOL;" 2>/dev/null || echo no-php'
done
# Which SAPI is serving web traffic (mod_php is highest risk)
apachectl -M 2>/dev/null | grep -i php
ls /etc/php/*/fpm 2>/dev/null
Then upgrade to 8.5.12, 8.4.27, 8.3.36 or 8.2.35 or later, and restart every worker. A package upgrade that leaves old PHP-FPM workers or Apache children running hasn't fixed anything yet.
# Debian/Ubuntu with distro or Ondřej Surý packages
sudo apt update && sudo apt install --only-upgrade 'php8.*'
sudo systemctl restart 'php*-fpm' apache2 2>/dev/null
# Official Docker images: rebuild on a fixed tag, for example
# FROM php:8.3.36-fpm (or a later patch release)
If you're on 8.1 or older, the PHP project won't ship a fix. Some distributions and long-term-support vendors provide backports, so check with yours, otherwise upgrade or take the system offline as CISA recommends (Canadian Centre for Cyber Security).
If you truly cannot patch for a few hours, CERT-EU lists partial mitigations. Each one blocks known paths, not every variant (CERT-EU):
- Set
memory_limit=-1in a way apps can't override, such asphp_admin_value[memory_limit] = -1in the FPM pool, and make sure nothing sets a finite limit back. This removes PHP's own memory cap, so pair it with a container or cgroup memory limit. CERT-EU warns not to useRLIMIT_ASorRLIMIT_DATAfor that, because they can trigger the vulnerable allocation failure. - Set a low
post_max_size(CERT-EU suggests8Mwithout uploads,32Mwith them) and keepupload_max_filesizebelow it. - Set
session.upload_progress.enabled=0inphp.inior the pool config, then restart workers.ini_set()and.user.inidon't take effect early enough. - On PHP 8.5, set
fatal_error_backtraces=0. - Hiding errors with
display_errorsor@does not help.
; /etc/php/8.x/fpm/pool.d/www.conf (temporary, not a fix)
php_admin_value[memory_limit] = -1
php_admin_value[session.upload_progress.enabled] = 0
php_admin_value[post_max_size] = 8M
php_admin_value[upload_max_filesize] = 4M
Calif's own advice on these settings is short: "These changes do not replace a patch" (Calif Research).
Mitigation: Fragnesia
Check whether your running kernel is fixed. "Installed" and "running" are different things until you reboot.
uname -r # running kernel
# Ubuntu/Debian: is a newer kernel installed but not booted?
ls /boot/vmlinuz-* | sort -V | tail -3
# RHEL/Alma/Rocky
rpm -q kernel | sort -V | tail -3
# Are the relevant modules loaded right now?
lsmod | grep -E '^(esp4|esp6|rxrpc) '
The real fix is a patched kernel plus a reboot. Until you can reboot, the distro-recommended mitigation is to stop the esp4, esp6 and rxrpc modules from loading. This breaks IPsec ESP (strongSwan and similar VPNs) and AFS, so don't apply it on hosts that use those (AlmaLinux, Ubuntu).
sudo tee /etc/modprobe.d/fragnesia.conf >/dev/null <<'CONF'
install esp4 /bin/false
install esp6 /bin/false
install rxrpc /bin/false
CONF
sudo rmmod esp4 esp6 rxrpc 2>/dev/null; true
# Ubuntu also recommends regenerating initramfs so early boot respects it
sudo update-initramfs -u -k all # Debian/Ubuntu only
If you suspect a host may already have been targeted, AlmaLinux suggests dropping the page cache so any tampered cached pages are evicted and the next read comes fresh from disk:
sudo sh -c 'echo 3 > /proc/sys/vm/drop_caches'
Red Hat adds that if IPsec has to stay up, general hardening that limits local access (SELinux enforcing, non-root workloads, restricted debug access) reduces risk (Red Hat).
Mitigation: GitLab and Ruby
# Omnibus/Linux package install
sudo gitlab-rake gitlab:env:info | grep -E 'GitLab information|Version|Ruby'
# Ruby version actually used by your app or source-built GitLab
ruby -v
You want GitLab 19.2.8, 19.3.5, 19.4.2 or later (and the later October 21 builds, which also fix CVE-2026-110264). If GitLab runs on a Ruby you supply, you also want Ruby 3.3.13, 3.4.12 or 4.0.9 or later (Ruby). On Alpine, apk upgrade brings in the fixed Ruby for 3.21 through 3.24 and edge.
For your own Ruby code, find places that compile regexes from input:
rg -n 'Regexp\.(new|union|compile)\(' app lib
rg -n '/[^/\n]*#\{[^}]+\}[^/\n]*/' app lib # interpolated regex literals
Detection: what to look for
These come from Dream's report, the ctxsec hunt summary and CISA's guidance. Treat them as starting points, not a complete list.
- PHP workers spawning shells or downloading tools. CISA calls this out directly and has published a SIGMA rule in its GitHub repository (CISA).
- Bursts of huge multipart POSTs with very long filenames and
session.upload_progressfields, followed by crashes or restarts of the PHP process (ctxsec). - A modified
/etc/pam.d/su, specifically one that usespam_permit.so. Compare it with a known-good copy from the same OS version. Keep in mind that Fragnesia writes into the page cache, so a reboot or cache drop can make the live file look clean again (AlmaLinux). Check before you reboot, and record what you find. - Small unexpected PHP files in web-reachable directories, and new systemd units or
rc.localentries that start SSH tunnels. - Directory activity from places it shouldn't come from, such as LDAP enumeration or account creation originating at a firewall or management server.
# Quick checks on a Linux host (read-only)
grep -n pam_permit /etc/pam.d/su
rpm -Vf /etc/pam.d/su 2>/dev/null # RHEL family: verify owning package
systemctl list-unit-files --state=enabled --no-pager | sort
sudo find /var/www -name '*.php' -mtime -14 -size -2k 2>/dev/null # small, recently changed PHP files
ps -eo user,pid,ppid,cmd --forest | grep -E -A3 'php-fpm|apache2|httpd' | grep -E '\b(sh|bash|curl|wget)\b'
If something turns up, treat the host as compromised: isolate it, preserve evidence, rotate any credentials it held (especially directory service accounts), and rebuild rather than clean.
Our take
Everything from here on is opinion.
The chain is the story, not either CVE. PHP2Shell alone gets an attacker a shell as nobody. Fragnesia alone needs an attacker already on the box. A month ago, a lot of teams would have triaged Fragnesia as "local only, patch at the next maintenance window" and plenty of those windows never came. This campaign shows what that triage costs: an old local bug turned a sandboxed foothold into root in the same request. We think "local privilege escalation" deserves a much higher default priority on any host that faces the internet or runs untrusted code, and that includes your container hosts and CI runners.
Your stack isn't your attack surface. We build mostly in Django and Python, and our first reaction to "PHP RCE" was that it isn't our problem. Then we thought about how many teams we know who run a WordPress site on the same VPS as their API, keep phpMyAdmin around for a legacy database, or have a firewall with a PHP-based admin UI. An honest inventory question is "what is listening on port 443 anywhere we own?", not "what language did we write the app in?"
Management interfaces don't belong on the internet. Dream's funnel started with roughly 19,000 PAN-OS management pages that anyone could reach. Every one of those exposures was a configuration choice. If the admin page for your firewall, database, CI server or internal tooling is reachable from the public internet, put it behind a VPN or an identity-aware proxy this week, whether or not you're affected by these specific bugs.
"Patched" means the running process is patched. Both of this week's KEV stories have that twist. PHP needs worker restarts. Kernel fixes need a reboot. GitLab's fix didn't apply to installs on a self-supplied Ruby. Alpine upgraded GitLab twice and was still exposed. For every fix, we'd verify from the outside in: the running kernel version, the PHP version a live request reports, and the Ruby version the GitLab process actually loaded.
On the AI angle, stay calibrated. It's easy to headline this as "AI hacks the government." Dream's own wording is more careful: AI tools were present, the code style suggests AI-assisted development, but there's no direct evidence the AI wrote the exploit, and it wasn't an autonomous attack. What we take from it is narrower and more useful. The slow, fiddly part of this exploit was building memory-layout profiles for each PAN-OS build. That's the kind of repetitive engineering work that AI tooling speeds up, which means the gap between "PoC published" and "weaponised for your specific appliance" is likely to keep shrinking. Patch timelines measured in weeks were already risky. They look worse now.
For Python developers, a related habit worth checking. Python's re module isn't Onigmo and isn't affected by the Ruby bugs. But the pattern behind the GitLab issue, compiling a regex from a string a user controls, is common in Python code too, and it has its own long-known risk of catastrophic backtracking. If you let users supply patterns, consider a linear-time engine such as the google-re2 package for those paths, plus a strict length limit and timeouts on the work around it.
A 30-minute checklist for today
- List every host and container that serves HTTP, and run
php -v(or the container loop above) on each. - Upgrade PHP to 8.5.12 / 8.4.27 / 8.3.36 / 8.2.35 or later, and restart all workers. If you can't, apply the CERT-EU mitigations and take anything on 8.1 or older offline.
- Run
uname -ron every Linux host. Reboot into a fixed kernel, or apply theesp4/esp6/rxrpcblock where you don't use IPsec or AFS. - Check
/etc/pam.d/suforpam_permit.sobefore rebooting anything you suspect. - Confirm self-managed GitLab is on 19.2.8 / 19.3.5 / 19.4.2 or later and, if it uses your own Ruby, on Ruby 3.3.13 / 3.4.12 / 4.0.9 or later.
- If you run PAN-OS, follow Palo Alto Networks' advisory.
- Take admin and management interfaces off the public internet.
Sources
- CISA: Critical Vulnerabilities in PHP Require Immediate Patching and KEV additions, October 29
- CERT-EU advisory 2026-018 on PHP
- Canadian Centre for Cyber Security AL26-026
- Calif Research: PHP2Shell
- Dream Research: Chained PHP2Shell Exploit Roots 25 Servers
- Palo Alto Networks advisory for CVE-2026-109618
- Linux kernel CVE announcement for CVE-2026-46300, AlmaLinux, Ubuntu, Red Hat RHSB-2026-003
- GitLab 19.4.2 patch release and Ruby's Regexp fixes post
- Reporting: BleepingComputer, Help Net Security
This post reflects public information as of October 30, 2026. Advisories are still being updated, so check the vendor links above for the latest fixed versions before you act.
About the author
- Custom software & AI development company. We build production-ready AI integrations, mobile apps, and web platforms for businesses that want real results. lycore.com
Comments
Join the discussionNo comments yet. Be the first to comment.



