The responders found no web shell. No backdoor, no scheduled task, no modified file left behind on disk, and an integrity check that came back clean. What the appliance had already given up was the LDAP service account it uses to read your directory, the Active Directory attributes holding LAPS local administrator passwords and BitLocker recovery keys, and eight rotating packet captures of its own LDAP traffic.
Volexity published that case on October 13 after investigating a customer's Ivanti Connect Secure VPN appliance. Ivanti issued a patch the same day for two of the three zero-days involved, tracked together as CVE-2026-104935. CISA added it to the Known Exploited Vulnerabilities catalog hours later with a due date of October 16 and a forensic triage requirement attached. The third bug, the one that turns code execution into root, has no patch and no CVE.
Whether this one is yours
You are in scope if you run Ivanti Connect Secure. If your remote access is a different product, this one is not yours and you can stop here.
The version ranges are the part worth reading twice. Ivanti's own CVE record lists everything before 25.1.3.0 as affected, with four builds named unaffected: 22.7R2.14 and prior, 22.8R2.5, 25.1.3.0 and 25.1.3.1. The appliance Volexity investigated was running 22.8R2.4, build 19097, which was the newest release available when the attacker reached it. Being current was not a defense here, and "we are fully patched" is not an answer to the question this story asks.
Ivanti scored the CVE at 8.6 on CVSS 3.1, with integrity and availability impact marked low. That score describes the authentication bypass on its own. CISA's own assessment in the same record is less equivocal: exploitation active, automatable yes, technical impact total. Read the second one when you set your priority.
Two details in the catalog entry are worth lifting out, because trackers tend to flatten them. The due date is three days, not fifteen, under BOD 26-04. And the entry carries a forensic triage requirement, which means CISA is telling federal agencies to collect evidence rather than only to patch. That instruction binds federal civilian agencies. For everyone else it is the clearest signal available about what CISA thinks the likelihood of prior compromise is, and it costs nothing to act on.
Three bugs reached through one request
The chain starts at a page every Connect Secure appliance serves before login. A request to /dana-na/auth/url_default/welcome.cgi passes the access check on its URL path, then an X-DSCGI-PATH header redirects it to a different CGI script behind the gate. That is the authentication bypass, CWE-290, spoofing.
The header pointed at wu.cgi, an upload handler that writes the uploaded file to disk first and validates it as a ZIP afterward, deleting it when the check fails. Between the write and the delete there is a window, and a second request timed into that window executes the file. Those two bugs together are what CVE-2026-104935 covers, and together they run code as the unprivileged cgi-server user.
The third bug does the rest. The appliance's dsinvoked broker trusts configuration data in DSCache to decide which binary runs a maintenance task. The exploit patches the in-memory copy of /home/lib/libdsplibs.so so that a legitimate task, ScriptUtil/updateOpswatSDK, resolves to the Perl interpreter instead, runs the attacker's script as root, and then puts the configuration entry back. Ivanti has said a fix is coming in a future release. Until it ships, any code execution on the appliance is root.
Volexity tracks the operator as UTA0576 and assesses with low confidence that the motive is criminal rather than espionage. It also assesses with medium confidence that the actor used large language models during exploit development and post-exploitation. That assessment is worth sitting with for a moment. Three chained zero-days in a hardened appliance used to be the signature of a small number of well-resourced teams, and the cost of producing one is the quiet assumption underneath most threat models. Treat that assumption as something to re-test rather than something to rely on.
What the Integrity Checker cannot see
Ivanti's standing guidance for this appliance is to upgrade and run the Integrity Checker Tool. That tool compares files on disk against known-good hashes, and this chain never changes a file on disk. The library patch lives in memory. The DSCache entry is restored. The payload is base64 and zlib-compressed Python passed to exec(), so it never lands as a script. Volexity states plainly that the tool misses this activity and that confirming a compromise requires network and memory forensics.
The attacker did not leave an implant either. Each command was delivered by re-running the whole chain over HTTPS, with results returned in the response. Then came the cleanup: selective truncation and rewriting of /home/runtime/dlogs/debuglog and /var/log/audit/audit.log, deletion of temporary files, output redirected to /dev/null.
Which leaves most teams in an unfamiliar position. The vendor tool returns green. The appliance has no agent on it. And the question your insurer or your board will ask is not whether you patched on October 14, but whether anything happened between October 3 and then.
The three places the evidence still lives
Your own web and proxy logs
This is the highest-value check and the only one an attacker could not reach, because it runs on infrastructure in front of the appliance rather than on the appliance itself. Volexity's signature is a mismatch: a request whose URL path is welcome.cgi carrying an X-DSCGI-PATH header that points somewhere else, in volume. If your reverse proxy, load balancer or WAF logs request headers, you have the answer in your own data.
# Reverse proxy / WAF access logs that capture request headers.
# Signal 1: any X-DSCGI-PATH value that does not match the requested path.
grep -aiE 'welcome\.cgi' /var/log/nginx/access.log* \
| grep -aiE 'X-DSCGI-PATH:\s*(/home/webserver/htdocs/dana/fb/smb/wu\.cgi|/home/runtime/tmp/)'
# Signal 2: volume. Normal welcome.cgi traffic is a trickle per source IP.
# The chain re-runs for every command, so one IP spikes.
awk '$0 ~ /welcome\.cgi/ {print $1}' /var/log/nginx/access.log* \
| sort | uniq -c | sort -rn | head -20
# Known attacker infrastructure from the Volexity report.
grep -aF '108.165.147.64' /var/log/nginx/access.log* /var/log/firewall/*.log
Set the window from October 1 onward; Volexity dates the activity on its customer to October 3 and does not claim that was the first instance anywhere.
The appliance itself, if you can still read it
Do this before you upgrade, because the upgrade is also a reboot and the evidence that matters most is in memory. If you have a support path to a shell or a Volexity-style capture, the named artifacts are specific: a hidden directory at /data/.icscap/, a renamed tcpdump running as dsstatmond, rotating capture files cap0 through cap7, a credential parser h.py alongside them, and exploit remnants matching /home/runtime/tmp/ics-root-run-inband-*.pl. Post-exploitation tooling sat in /tmp/ under short names including sp_run.py, spray_hits.json and pu.txt.
Gaps count as findings. A debug log that jumps, an audit log with a hole in it, or a file whose timestamps do not line up are results, not noise.
Your directory, which is where the attack actually went
The appliance was a doorway. The work happened against Active Directory, using the credentials the appliance stores, and that traffic crossed your network where your controls can see it. A VPN concentrator making LDAP queries is normal. A VPN concentrator Kerberoasting, reading ms-Mcs-AdmPwd or BitLocker recovery attributes, and then spraying passwords is not.
-- Microsoft Sentinel / Defender XDR: directory activity sourced from the
-- VPN appliance's own IP in the exposure window. Replace the address.
let vpn_appliance = "10.0.0.0"; // your Connect Secure internal interface
SecurityEvent
| where TimeGenerated between (datetime(2026-10-01) .. now())
| where IpAddress == vpn_appliance
| where EventID in (4768, 4769, 4771, 4625, 4624)
| summarize
attempts = count(),
accounts = dcount(TargetUserName),
failures = countif(EventID in (4625, 4771)),
rc4_tickets = countif(EventID == 4769 and TicketEncryptionType == "0x17")
by bin(TimeGenerated, 1h)
| where accounts > 10 or rc4_tickets > 0
| order by TimeGenerated asc
Ten distinct target accounts in an hour from a device that should only ever bind as one service account is the shape of the spray. RC4 service tickets from the same source are the shape of the Kerberoast. Domain controller logs you cannot search within the hour make this question unanswerable, and that gap will outlast this CVE.
Rotate the directory credentials the appliance stores
Order the next two days like this. Upgrade to 22.8R2.5 or 25.1.3.1, after you have captured whatever appliance evidence you can reach, and accept that the privilege escalation stays open until Ivanti ships its fix. Then treat the LDAP service account as burned: rotate it, and check what it could actually read, because an account provisioned with more directory rights than a VPN needs is the reason this incident reached LAPS passwords and BitLocker keys at all. Rotate anything the AD queries exposed, which means LAPS passwords on the machines that were enumerated and any account that showed up in a spray hit. Terminate active VPN sessions so stolen session material stops working.
The judgment call is the last one. Patching closes the door; it does not tell you whether anyone walked through it between October 3 and the day you upgraded. On an appliance with no agent, no file-level evidence and a vendor tool that reports clean either way, that answer comes from your own proxy logs and your own domain controller telemetry or it does not come at all. If you cannot reconstruct that window, work the directory side as though it happened.
Not sure what your VPN appliance did between October 3 and the day you patched?
We help organizations reconstruct the exposure window on appliances that carry no agent and no file-level evidence, using the proxy and directory telemetry they already have, and we scope the credential rotation that follows. Book a session with our team to work through your remote-access estate.
