PoisonedRefresh: The F5 BIG-IP Rootkit That Hides Its Web Shell Where File Scanners Can't See
A Linux rootkit on compromised F5 BIG-IP APM appliances hooks Apache's PHP loader to inject a web shell straight into memory, leaving the on-disk webtop scripts byte-for-byte clean. It's tied to the exploited CVE-2025-53521, and it's a working blueprint for fileless persistence on the access gateways guarding enterprise identity.
HTTP 201, Content-Type: text/css. That's what the web shell hands back to its operator, dressed up to look exactly like a BIG-IP appliance serving a stylesheet for its own login portal. Nothing about that response looks wrong in a packet capture. Nothing about the PHP file it came from looks wrong on disk, either — because the file on disk never changed. That's the whole trick, and it's a good one.
I run access gateways for a living. Not F5 specifically at the moment, but the category — the box that sits between the internet and your identity provider, terminates the access policy, and decides who gets a session. When one of those boxes gets a rootkit that defeats file-integrity monitoring by design rather than by accident, I want to understand exactly how, because the answer tells you what your own detection stack is quietly assuming and getting wrong.
What Actually Happened
On September 7, 2026, SophosLabs published an analysis of a Linux implant pulled from a compromised F5 BIG-IP Access Policy Manager (APM) appliance. Sophos tracks the family as Linux/Agnt-IC. ESET had already seen related samples back in April 2026 and named it PoisonedRefresh. F5 itself refers to the associated activity cluster as c05d5254 in its own indicator-of-compromise documentation.
The headline detail is simple to state and slightly unsettling to sit with: when Apache loads one of three specific PHP scripts belonging to the APM webtop, the malware inserts a web shell into the copy of that script held in memory. The file sitting on the appliance's disk is never touched. A file-integrity scan, a hash comparison, a checksum against a known-good baseline — all of it comes back clean, correctly, because the thing it's checking genuinely hasn't changed.
F5 named those exact three files back in March 2026, in a published list of indicators of compromise for the c05d5254 activity, and explicitly said at the time that changes to them alone don't indicate a breach. The Sophos analysis explains why F5 had to hedge that IOC so carefully: the files often don't change at all. F5 has since acknowledged it has also seen cases where a web shell does land on disk, but the in-memory-only variant is the one that makes every prior assumption about “check the files” fall apart.

Figure 1: The three webtop scripts — apm_css.php3, full_wt.php3, and webtop_popup_css.php3 — that stay byte-for-byte unchanged on disk even when the in-memory rootkit is active. (Source: The Hacker News)
The Vulnerability Underneath It: CVE-2025-53521
PoisonedRefresh isn't the initial access vector. It's what shows up after initial access, on appliances where an APM access policy is bound to a virtual server. The door it walks through is CVE-2025-53521, and its history is worth laying out because the timeline itself is a lesson in why “already patched” doesn't mean “already safe.”
- F5 first published the flaw on October 15, 2025, describing it only as a denial-of-service issue.
- On March 27, 2026, F5 reclassified it after new evidence: it's actually unauthenticated remote code execution, and it had already been exploited in the wild. CVSS 3.1 score: 9.8. CVSS 4.0: 9.3.
- CISA added it to the Known Exploited Vulnerabilities catalog the same day, giving U.S. federal civilian agencies until March 30 to remediate — a three-day window that tells you how seriously the reclassification was taken.
- The Shadowserver Foundation counted roughly 795 internet-exposed endpoints still vulnerable to the flaw around the time of disclosure.
The fix, in other words, has been sitting in the October 2025 release notes for nearly a year under the wrong severity label. Anyone who triaged it as “just a DoS” and deprioritized the patch was making a completely reasonable call with the information available at the time — and got burned five months later when the real classification landed.
| Versions known to be vulnerable | Fixed in |
|---|---|
| 17.5.0 – 17.5.1 | 17.5.1.3 |
| 17.1.0 – 17.1.2 | 17.1.3 |
| 16.1.0 – 16.1.6 | 16.1.6.1 |
| 15.1.0 – 15.1.10 | 15.1.10.8 |
Both Ireland's NCSC and the UK's NCSC have since weighed in with the same underlying message: the October 2025 patch is valid and does stop exploitation of CVE-2025-53521 going forward, but nobody has published a timeline for when exploitation actually started, and Ireland's NCSC explicitly expects some exploitation happened before the flaw was ever disclosed.
The Kill Chain
Strip away the branding and this is a fairly classic two-stage Linux implant, but the second stage is doing something I haven't seen documented this cleanly on a network appliance before. Here's the sequence Sophos and ESET reconstructed between them.
- Initial exploitation. An attacker hits an unpatched BIG-IP APM appliance with CVE-2025-53521 — unauthenticated, no login required — against a virtual server that has an APM access policy configured.
- Stage one drops, disguised as a system binary. Sophos found the installer hidden inside a sample named
umount. It's built to run as root and immediately disables SELinux. - The installer infects
/usr/sbin/httpd. It prepends malicious code to the front of the real Apache binary. Sophos matched the size of the injected code to the payload carried inside the installer — strong evidence the installer itself is doing the stitching. - Persistence extends into the install media. ESET found the same installer also infecting
umount,httpd, andrc.localinside BIG-IP install images, presumably so the infection rides along through reinstalls and upgrades rather than getting wiped by them. - Execution hijack before Apache's own code runs. Because the malicious code sits at the front of the httpd binary, it executes before Apache's main program logic starts — effectively hooking process startup ahead of the application itself.
- The APR hook. The implant hooks
apr_dso_load, the Apache Portable Runtime function responsible for loading shared modules, and then does nothing until it sees Apache loadlibphp— the PHP module itself. - Runtime memory patching. Once
libphpis loaded, the malware reads/proc/self/mapsto locate the module's address range, briefly flips those memory pages writable, rewrites the internal calls PHP uses to open, size, and map files, then restores the original page permissions. From this point it controls what PHP sees whenever one of the three target scripts is requested. - In-memory injection. When
apm_css.php3,full_wt.php3, orwebtop_popup_css.php3gets mapped into memory to serve a request, the rootkit places the web shell content in front of the original script content — in RAM only. The file on disk is never opened for writing, never modified, never touched. - The web shell itself. It reads the raw body of an incoming request, checks for a short marker value, and if present, decrypts the remainder and executes it. The response comes back as HTTP 201 with a
text/csscontent type, mimicking an ordinary stylesheet fetch. - A second, separate access path. The malware also opens a local Unix domain socket at
/run/bigtlog.pipe. After validating a fixed token, it wires that socket straight to/bin/bash— an interactive shell with no listening TCP port anywhere. Sophos couldn't find code in its sample that actually connects to this socket, so it looks like a separate, possibly unfinished or operator-only feature rather than something the web shell itself uses.
Why File Integrity Monitoring Doesn't Catch This
File-integrity monitoring works on a premise: the thing an attacker wants you to run is the thing sitting in the file. Compare the file against a known-good hash, and you'll notice if it's been swapped or appended to. That premise has held up reasonably well for two decades of web shells, because writing a payload into a file and getting a web server to execute it has, historically, required the payload to actually be in the file.
PoisonedRefresh breaks that premise at the layer below the file: it doesn't change what PHP reads from disk, it changes what PHP believes it read from disk, by patching the module that does the reading. The script's bytes on the filesystem are irrelevant once libphp's own file-open and mmap routines have been quietly rewired. Your FIM tool is asking the right question of the wrong layer.
This is also why Sophos frames every individual signal it lists as a lead rather than proof. No single artifact — not the pipe file, not a failed sys-eicheck, not an audit log line — is definitive on its own. You need file, process, and memory evidence together, because the entire design goal of this implant was to make the file layer lie convincingly.
Indicators Worth Hunting For
| Type | Indicator |
|---|---|
| File | /run/bigtlog.pipe or /run/bigstart.ltm present |
| Binary | Hash, size, or timestamp mismatch on /usr/bin/umount or /usr/sbin/httpd against a known-good copy (sizes/timestamps legitimately differ across releases and hotfixes) |
| Tool | sys-eicheck failing specifically because one of those two binaries changed |
| Log | Entry in /var/log/restjavad-audit showing a local user reaching the iControl REST API from localhost |
| Log | Entry in /var/log/auditd showing SELinux disabled via that same route |
| Log | Entry in /var/log/audit showing a bash command run through iControl REST, writing base64 data to a file and executing /run/bigstart.ltm |
| Traffic | HTTP 201 responses carrying a text/css content type from the appliance |
| Host behavior | An Apache worker reading /proc/self/maps, altering memory permissions around libphp, binding a socket under /run, or spawning /bin/bash |
| Hash | SHA-256: 26bd5b0722d1dbab5db749a063c49bc8638653ac2addfead7a9cb3d6d57bccc9 |
| File, weak alone | Changes to the three .php3 scripts — presence of a change is meaningful, but absence of one proves nothing |
My Take
What bothers me most about this isn't the cleverness of the hooking — APR module interception and runtime mmap patching are well-understood rootkit techniques, just rarely aimed at a webtop portal. What bothers me is where it's deployed. APM isn't some peripheral service on these boxes; on a lot of the environments I've touched, it is the access control plane. It's evaluating access policies, brokering sessions, and often sitting directly in the authentication path for VPN-less remote access into internal applications. A rootkit that can inject an authenticated-looking web shell into that exact process, with zero on-disk footprint in the scripts everyone already agreed not to worry about, isn't a curiosity. It's a working demonstration that “network appliance” and “black box we trust because the vendor manages it” is a dangerous pairing once the vendor's own IOC guidance turns out to be an attack surface.
The part I keep coming back to is that F5's March IOC list telling defenders “these three files changing doesn't necessarily mean compromise” was accurate and responsible guidance at the time, based on the cases F5 had actually investigated. It just happened to describe exactly the blind spot this implant was built to exploit. That's not a knock on F5's incident responders — it's a reminder that IOC lists for appliance firmware are inherently reactive, and the attacker read the same public guidance the defenders did.
I'd also flag the DoS-to-RCE reclassification gap as the more structurally worrying issue of the two. A nine-month stretch where a critical unauthenticated RCE sat in a ticket queue labeled “denial of service” is a patch-prioritization failure mode that no amount of rootkit analysis fixes after the fact. If your vulnerability management program ranks appliance CVEs purely by the severity label at disclosure time rather than re-checking when vendors revise their own advisories, this is the exact scenario that gets you compromised on a technically “already assessed” finding.
What You Should Do Right Now
If you run BIG-IP APM anywhere in your environment — whether or not you've already applied the October 2025 fix — treat this as an active hunt, not a patch-and-move-on ticket:
- Confirm your version is actually fixed. Check against the table above. 17.5.1.3, 17.1.3, 16.1.6.1, and 15.1.10.8 are the floors, not the ceiling — run current.
- Run
sys-eicheckand treat a failure as a live incident, not a false positive to suppress. F5's own indicators say infection ofumountandhttpdmakes this integrity tool fail — that failure is the alarm, not noise to tune out. - Pull a
qkviewand open a case with F5 regardless of what your local scan shows. Ireland's NCSC specifically notes F5 can check that report for compromise indicators you can't see locally, and that an associated support case gets a faster, fuller response than a self-service triage. - Compare loaded modules in memory against the on-disk copies as a standing step in your incident response playbook for any internet-facing web server, not just BIG-IP. Sophos calls this out explicitly, and it's good practice generally — disk-only integrity checking is now a demonstrated gap, not a theoretical one.
- Grep your audit logs for iControl REST commands run from localhost, particularly anything writing base64-encoded data to disk or invoking
/run/bigstart.ltm. This is the persistence mechanism, not a side effect. - Watch for
/run/bigtlog.pipeand outbound HTTP 201 responses with atext/csscontent type in appliance traffic logs — add both as detection rules even if you find nothing on the first pass. - If you can't run a full investigation, isolate and rebuild the appliance from a known-clean image rather than trusting a clean-looking file scan. The UK NCSC's advice here is blunt for a reason: given that this implant is designed to survive upgrades via infected install media, a rebuild needs to come from media you can independently verify as clean, not just a newer version number.
- Don't assume “we patched in October” clears you. Patching stops new exploitation of CVE-2025-53521; it says nothing about whether the box was already compromised before you patched. Hunt regardless of patch date.
None of this is exotic advice. It's the same discipline any of us would apply to a compromised Linux web server — the only real adjustment is remembering that the appliance running your access policies is exactly that: a Linux web server, with all the same rootkit techniques available to whoever gets code execution on it, plus the added disadvantage that most of us monitor it far less closely than we monitor our actual application servers.
This article was researched and drafted with AI assistance as part of an experiment in building a faster tech-writing workflow, then reviewed and edited before publishing.
Primary source: F5 BIG-IP APM Malware Injects a PHP Web Shell Into Memory, Evading Disk Scans — The Hacker News, September 2026
Secondary sources: Dissecting a PHP web server rootkit — SophosLabs, September 7, 2026; ESET Research on PoisonedRefresh — ESET, April 2026; K000156741 Advisory and K000160486 Indicators of Compromise — F5
Keep Reading
One endsWith Call Away From Root: The Kestra Auth Bypass Attackers Are Already Using
Kestra's auth filter whitelisted /configs with a suffix match instead of an exact one, so any API path ending in "configs" skipped authentication — and since Kestra runs shell and Python scripts by default, that one line is unauthenticated root RCE. CISA confirms it's already being used for reverse shells and cryptomining.
Cisco's Firewall Manager Had a CVSS 10 Auth Bypass — And Attackers Were Already Inside
CVE-2026-20079 lets an unauthenticated attacker get root on Cisco Secure Firewall Management Center, the box that manages your entire firewall fleet. CISA gave federal agencies one day to patch it.
SonicWall's SMA 1000 Keeps Falling to the Same Trick — Twice in Three Months
SonicWall's SMA 1000 remote access gateways were hit by two separate unauthenticated RCE chains in 2026, both built on the same confused-deputy design flaw. Here's the kill chain, the ransomware fallout, and what it means if you run one of these boxes.