Your change ticket says “NetScaler upgraded to the fixed build, vulnerability closed.” Your vulnerability scanner agrees. Your CISA KEV SLA is green. And there may be a PHP file in a web directory on that appliance that was dropped on September 5, survived the upgrade, and is still answering POST requests from someone who is not you.
That isn’t a hypothetical. It is the explicit warning in the Unit 42 brief on the NetScaler zero-days, and the pattern repeats across the three edge-device campaigns that landed between late August and mid-October 2026. This piece is a position and a procedure. The position: for any internet-facing appliance that was exploitable before a patch existed, “patched” is a statement about the vulnerability, not about the device. The procedure: what to do on the device after the upgrade, given that each of these three campaigns defeats a different part of the standard playbook.
Sourcing note: the details below come from vendor advisories, Volexity, Google Threat Intelligence, and Unit 42 as relayed by secondary coverage. I could not pull every primary report directly, and sources disagree on some dates; where they do, I say so.
Three campaigns, three persistence models
Ivanti Connect Secure: no implant at all
On October 13, Ivanti disclosed CVE-2026-104935 and Volexity published its analysis the same day. Volexity reports a previously unknown actor, UTA0576, chaining three flaws from October 3 (one write-up suggests activity from October 1; Volexity’s own date is the 3rd) to get unauthenticated root code execution on Connect Secure VPN appliances:
- An authentication bypass.
- A remote code execution bug (these first two are what CVE-2026-104935 covers).
- A privilege escalation in the
dsinvokedbroker, which trusts cached configuration data (DSCache).
Ivanti shipped fixes for the first two on October 13 (22.8R2.5, 25.1.3.0, 25.1.3.1, 25.1.1.2 per BleepingComputer). The privilege escalation was reported as still unfixed, with a patch promised “in the coming weeks.” CISA put the CVE in KEV the same day.
What makes this campaign instructive is what the attacker did not do. Per Volexity there was no web shell and no backdoor. Commands and their output rode inside the exploit chain’s own HTTPS requests; the attacker re-exploited the device for every command. Code was patched in memory only, and logs were wiped. Ivanti’s Integrity Checker Tool, which checks the filesystem, is blind to exactly this.
Think about what that does to your assumptions:
- No file IOCs. There is nothing to grep for on disk.
- No persistent C2. There is no beacon to find in egress logs, just inbound requests that look like exploitation attempts.
- In-memory modification dies at reboot. That helps defenders only if you know to look before you reboot, and a patch-and-reboot workflow destroys the one place the evidence lives.
The consequence for the response: patching an Ivanti box tells you the next exploitation attempt fails. It tells you nothing about the credentials, session tokens, and config secrets the actor read while the box was vulnerable. And for the privilege-escalation link that may still be unfixed, a fixed build blocks the chain only because steps one and two are closed; treat the box as one foothold away from root until Ivanti ships the third fix.
BeyondTrust Remote Support: the classic, and the SUID bit
Google Threat Intelligence attributes exploitation of CVE-2026-102764 (CVSS 9.3, improper origin validation in an internal messaging component, reported against 26.2.3 and earlier) to ARMORY CASTLE, a suspected PRC-nexus actor formerly tracked as UNC5221. One summary dates first exploitation to August 29; BeyondTrust’s SaaS tenants were fixed automatically around October 9, while self-hosted customers must apply BT26-05 themselves. Forty days is a long exposure window for the self-hosted population.
The post-exploitation chain is textbook and therefore detectable:
- A lightweight PHP web shell, SAWFLY, appears in the web root roughly eleven minutes after the initial exploit. Reported filename:
mangoapi.php. - It only responds to a POST containing a specific obfuscated parameter and returns a stock 404 otherwise, which defeats naive scanners and curious requests.
- Commands arrive encoded and encrypted.
- The shell runs as an unprivileged service account, so the actor copies a system shell to a daemon-looking path and sets root ownership plus the SUID bit.
The takeaway is that this one can be found on disk. Which is exactly why it has a different failure mode: teams find nothing in a web log review (404s everywhere) and conclude they are clean.
NetScaler: the fixed build doesn’t clean the file system
Citrix disclosed CVE-2026-88771 (unauthenticated command injection) and CVE-2026-88772 (a memory overflow in DTLS configuration giving RCE or DoS), both CVSS v4 9.5, inside a bulletin covering CVE-2026-88771 through -88778. Unit 42 reported reconnaissance from late August, confirmed exploitation from about September 3, a DTLS-focused campaign running roughly September 4 to 24 with infrastructure rotating daily, and just over 50,000 internet-visible instances as of September 27. There was no workaround for the command injection; the interim advice was source-IP restriction or pulling the appliance off the internet.
Two delivery chains were observed pre-disclosure: one dropping .deb packages through the DTLS bug, one a three-stage command-injection sequence leaning on log poisoning of Perl scripts. Recovered tooling included a PHP web shell using RC4 for C2, plus Apache config tampering, PHP-engine enablement on a platform that does not normally serve PHP, and shell masking behind CSS-looking aliases.
Unit 42’s warning is blunt: updates “will not remove access for attackers that have already established persistence.” A firmware upgrade replaces the system image components it knows about. It does not know about the extra files an attacker put in writable paths.
Why the standard playbook fails
The standard playbook is: ingest advisory, prioritize by KEV, patch inside SLA, close ticket. It encodes one assumption, that exploitation begins at disclosure. All three of these campaigns began weeks before the advisory:
| Campaign | First exploitation (reported) | Public fix | Exposure before fix |
|---|---|---|---|
| BeyondTrust | ~Aug 29 | ~Oct 9 (SaaS) | ~6 weeks |
| NetScaler | ~Sep 3 (recon earlier) | Sept bulletin | weeks |
| Ivanti | Oct 3 (maybe Oct 1) | Oct 13 (partial) | ~10 days |
During that window your SLA was zero days old and meaningless. Your patch deadline didn’t protect you; only the absence of attacker interest did, and that is not a control.
The second failure is verification. Teams verify patches by checking a version string. That verifies the vulnerable code is gone and says nothing about what the vulnerable code let someone do. For a pre-auth RCE on a device that terminates VPNs or proxies remote support sessions, the right question isn’t “is it fixed” but “was it used, and what did it touch.”
The post-patch compromise assessment
Treat any appliance that was internet-exposed and running an affected version during a zero-day window as potentially compromised until you have positive evidence otherwise. The order below matters, because step one is the one patch-and-reboot workflows destroy.
1. Preserve before you patch (or before you reboot)
For Ivanti-style in-memory tampering, the reboot is the eraser. If you have not yet upgraded:
- Take the vendor’s snapshot/diagnostic bundle and any external integrity-check output.
- Export logs off-box now; the actor wipes the on-box copies.
- Capture a VM snapshot if virtual, including memory if your hypervisor supports it.
- Only then upgrade.
If you’ve already rebooted, say so in the incident record and move to step 2; absence of evidence from a rebooted box is weak.
2. Hunt the filesystem for what the advisories describe
For web-shell-style persistence (BeyondTrust, NetScaler), look for files that should not exist and permissions that should not be set. On a shell with root access to the appliance’s underlying OS (via vendor support channel if the platform is locked down):
| |
Specific things to look for, based on the public reporting:
- BeyondTrust:
mangoapi.phpor any PHP file in the web root with a recent mtime you cannot map to an upgrade; any root-owned SUID copy ofsh/bashoutside/bin. - NetScaler:
.debpackages you did not install; PHP enablement and Apache config modified since late August; web-served files with CSS-style names (.css,.woff) that contain script code. - General: compare file hashes against a freshly built appliance of the same version. A clean reference is worth more than any IOC list.
3. Hunt the access logs for exploitation, not implants
For the Ivanti-style fileless case, the evidence is request-shaped. You are looking for repeated unauthenticated requests to the same management or API endpoints from a single source, each followed by a response with an unusually large or structured body, in a burst pattern consistent with “one command per request.” Exact paths are in the vendor and Volexity indicators; load them from the primary source into your SIEM rather than from a blog.
A generic Sigma-style starting point that does not depend on vendor-specific paths:
| |
Tune it per device. The point is the shape: many successful, unauthenticated, state-changing requests from one address.
4. Assume credential exposure and rotate by blast radius
A compromised VPN or remote-support appliance is a credential vault. Rotate in this order:
- Secrets stored or cached on the appliance: LDAP/AD bind accounts, RADIUS shared secrets, SAML signing keys and IdP certificates, API keys for integrations.
- Sessions: invalidate all active VPN/remote-support sessions and force re-authentication with MFA re-enrolled where tokens were device-bound.
- Accounts that logged in through the appliance during the window, prioritizing admins and service accounts.
- The bind account’s reachable scope: assume anything it could read, the actor read.
Rotating the appliance’s admin password without rotating the bind account is theater.
5. Decide: clean, or rebuild
For the three cases above:
| Evidence | Action |
|---|---|
| No exposure during the window (patched/ACL’d before first exploitation) | Document, patch, move on |
| Exposed, logs and filesystem clean, but fileless-capable exploit (Ivanti) | Rebuild from known-good image, rotate secrets; clean logs prove little because the actor wipes them |
| Exposed, any web shell or SUID artifact found | Full rebuild, treat adjacent segment as compromised, run broader hunt |
| Exposed, filesystem clean, logs intact and clean | Rebuild anyway if the device holds high-value secrets; otherwise heightened monitoring |
I’m opinionated here: for a device that holds domain bind credentials and sat exposed to an unauthenticated root exploit for ten days or more, rebuild is the default and cleanliness is the exception that needs justification. The cost of a rebuild is a maintenance window. The cost of being wrong is a persistent foothold at the network edge with credentials to everything behind it.
What to change before the next one
The assessment above is expensive every time. Three changes make it cheaper.
Shrink the exposed surface to what must be exposed. Management interfaces, remote-support consoles, and admin APIs have no business answering the whole internet. NetScaler’s own interim guidance, source-IP restriction, would have changed the outcome for any team that already had it. Put admin planes behind a separate, ACL’d path.
Keep a known-good reference. Maintain a built-from-image twin of each appliance model and version so file-system diffs are a five-minute job. Without one, you are reading file lists by eye.
Ship logs off-box in real time. Actors that wipe on-box logs cannot wipe your syslog server. For Ivanti-style tradecraft, off-box logs are the only reliable record. Make this a deployment requirement, not a hardening-guide suggestion.
Pre-authorize the rebuild. The slow part of rebuilding is getting permission. Write the runbook, get sign-off from change management in advance for “edge appliance, zero-day window, indicators present or inconclusive,” and exercise it.
Takeaways
- The SLA clock starts at first exploitation, not at disclosure. In all three campaigns that was weeks earlier.
- A version check verifies the vulnerability is closed. It does not verify the device is clean.
- Fileless, log-wiping tradecraft (Ivanti) defeats file-integrity tooling by design; preserve memory and off-box logs before you reboot.
- Web-shell tradecraft (BeyondTrust, NetScaler) is findable on disk, but only if you look for stealth behavior (404-on-no-parameter, SUID shells, PHP on a non-PHP platform).
- Rotate everything the appliance could read, starting with bind accounts and signing keys.
- For credential-holding edge devices exposed during a zero-day window, rebuild by default.
Three vendors, three persistence models, one outcome: the upgrade fixed the bug and left the visitor. Plan accordingly.