Back to Blog
PoisonedRefresh: The F5 BIG-IP Rootkit That Hides Its Web Shell Where File Scanners Can't See

PoisonedRefresh: The F5 BIG-IP Rootkit That Hides Its Web Shell Where File Scanners Can't See

September 15, 2026
12 min read
10 views
Share:

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 vulnerableFixed in
17.5.0 – 17.5.117.5.1.3
17.1.0 – 17.1.217.1.3
16.1.0 – 16.1.616.1.6.1
15.1.0 – 15.1.1015.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.

  1. 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.
  2. 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.
  3. 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.
  4. Persistence extends into the install media. ESET found the same installer also infecting umount, httpd, and rc.local inside BIG-IP install images, presumably so the infection rides along through reinstalls and upgrades rather than getting wiped by them.
  5. 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.
  6. 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 load libphp — the PHP module itself.
  7. Runtime memory patching. Once libphp is loaded, the malware reads /proc/self/maps to 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.
  8. In-memory injection. When apm_css.php3, full_wt.php3, or webtop_popup_css.php3 gets 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.
  9. 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/css content type, mimicking an ordinary stylesheet fetch.
  10. 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

TypeIndicator
File/run/bigtlog.pipe or /run/bigstart.ltm present
BinaryHash, 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)
Toolsys-eicheck failing specifically because one of those two binaries changed
LogEntry in /var/log/restjavad-audit showing a local user reaching the iControl REST API from localhost
LogEntry in /var/log/auditd showing SELinux disabled via that same route
LogEntry in /var/log/audit showing a bash command run through iControl REST, writing base64 data to a file and executing /run/bigstart.ltm
TrafficHTTP 201 responses carrying a text/css content type from the appliance
Host behaviorAn Apache worker reading /proc/self/maps, altering memory permissions around libphp, binding a socket under /run, or spawning /bin/bash
HashSHA-256: 26bd5b0722d1dbab5db749a063c49bc8638653ac2addfead7a9cb3d6d57bccc9
File, weak aloneChanges 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:

  1. 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.
  2. Run sys-eicheck and treat a failure as a live incident, not a false positive to suppress. F5's own indicators say infection of umount and httpd makes this integrity tool fail — that failure is the alarm, not noise to tune out.
  3. Pull a qkview and 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.
  4. 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.
  5. 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.
  6. Watch for /run/bigtlog.pipe and outbound HTTP 201 responses with a text/css content type in appliance traffic logs — add both as detection rules even if you find nothing on the first pass.
  7. 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.
  8. 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

Comments