← Back to Feed CACHED · 2026-10-15 16:46:53 · CACHE_KEY CVE-2026-104935
CVE-2026-104935 · CWE-290 · Disclosed 2026-10-13

Ivanti Connect Secure Authentication Bypass by Spoofing Leading to Unauthenticated RCE

ASSESSED — NOISGATE
Vendor
—
—
—
Reassessed
—
—
—
Verdict: —
Do you agree?
01 · The Real Story

Someone found the master key to the front gate of the castle and has been letting themselves in while you slept

CVE-2026-104935 is an authentication bypass by spoofing (CWE-290) in Ivanti Connect Secure — the SSL-VPN appliance that sits at the internet edge of thousands of enterprise networks. The flaw is embarrassingly simple: the welcome.cgi endpoint is publicly reachable without credentials, but the appliance honors an X-DSCGI-PATH HTTP header to decide which CGI handler actually processes the request. An attacker sends a request to the unauthenticated welcome.cgi with an X-DSCGI-PATH pointing at an *authenticated* handler like wu.cgi — and the appliance obeys, executing the authenticated handler without checking credentials. From there, a race condition in the file-upload handler lets the attacker drop and execute a Perl payload before validation can delete it, achieving arbitrary command execution. Chained with a separate, still-unpatched privilege-escalation bug in the dsinvoked service, the attacker reaches root. Affected versions include ICS 25.1.1.1 and prior, 25.1.2.2 and prior, and 22.8R2.4 and prior. The legacy 22.7R2.x branch is *not* affected. Fixed versions are 25.1.3.0 / 25.1.3.1, 25.1.1.2, and 22.8R2.5.

Ivanti scored this HIGH 8.6 — and that is a dangerous understatement. The CVSS vector (AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:L/A:L) rates Integrity and Availability impacts as Low, which ignores what actually happens after exploitation: the attacker chains to root, dumps every LDAP-stored domain credential from the appliance (including LAPS local admin passwords and BitLocker recovery keys), runs Kerberoast attacks, conducts password spraying against Active Directory, and captures plaintext credentials from LDAP and HTTP Basic Auth traffic passing through the appliance. Volexity documented threat actor UTA0576 doing exactly this since early October 2026 — a full week before the patch dropped. CISA added this to KEV on day zero of disclosure with a remediation deadline of October 16, 2026 (today). This is a CRITICAL vulnerability on a perimeter device that was exploited before anyone could patch it.

"Unauthenticated root RCE on your internet-facing VPN gateway, exploited as a zero-day. Patch now or assume breach."
02 · The Attack Path

5 steps from start to impact.

STEP 01

Target Discovery

The attacker identifies internet-facing Ivanti Connect Secure appliances using Shodan, Censys, or FOFA queries. Shadowserver reported 5,500+ exposed ICS appliances as of October 2026; Censys showed approximately 27,000 instances. The ICS login page at /dana-na/auth/url_default/welcome.cgi is trivially fingerprinted. Previdian observed 57 unique attacker IPs scanning across 5 countries within 48 hours of disclosure.
Conditions required:
  • Target must be running ICS version in affected range (pre-25.1.3.0)
  • ICS must be reachable from the internet (true by design — it is a VPN gateway)
Where this breaks in practice:
  • None — ICS appliances are internet-facing by architectural necessity
Detection/coverage: Shodan/Censys queries for http.html:"welcome.cgi" or http.title:"Ivanti Connect Secure". Nuclei template available since Oct 14, 2026 at projectdiscovery/nuclei-templates.
STEP 02

Authentication Bypass via X-DSCGI-PATH Header Spoofing

The attacker sends an HTTP request to the unauthenticated endpoint /dana-na/auth/url_default/welcome.cgi with a crafted X-DSCGI-PATH header pointing to an authenticated CGI handler such as /home/webserver/htdocs/dana/fb/smb/wu.cgi. The appliance validates the *URL path* for access control but routes the request based on the *header value* — a classic TOCTOU design flaw. This reaches the authenticated wu.cgi file-upload handler without any credentials. Weaponized tools: ApexBreach PoC (python3 exploit.py --url https://target --command "whoami"), 0x45dd detection tool on Sploitus.
Conditions required:
  • HTTPS connectivity to the ICS appliance on port 443
Where this breaks in practice:
  • None — single HTTP request, no credentials, no user interaction, low complexity
Detection/coverage: Monitor web logs for X-DSCGI-PATH header presence in requests to welcome.cgi. Alert on any request where the URL path and the routed CGI handler differ. WAF rules blocking X-DSCGI-PATH header in inbound requests would prevent this step.
STEP 03

File Upload Race Condition → Web-User RCE

The attacker uploads a Perl script with a .zip extension to bypass file-type validation in the wu.cgi handler. The handler writes the file to /home/runtime/tmp/<MD5(filename)> *before* validation occurs. Validation then detects the invalid content and deletes the file — but there is a narrow race window between write and deletion. The attacker sends parallel requests to hit that window and execute the uploaded Perl payload before it is deleted. The Perl script spawns Bash, which decodes a Base64/zlib-compressed Python payload for memory-only execution, leaving minimal disk forensic artifacts.
Conditions required:
  • Successful authentication bypass from step 2
  • Race condition window exists (inherent to affected versions)
Where this breaks in practice:
  • Race condition requires parallel requests, but public PoCs automate this reliably
Detection/coverage: File integrity monitoring on /home/runtime/tmp/. Unusual .zip file creation followed by rapid deletion in ICS temp directories. Process monitoring for unexpected Perl → Bash → Python execution chains.
STEP 04

Privilege Escalation to Root via dsinvoked

From web-user context, the attacker exploits the dsinvoked privileged service by performing in-memory patching of /home/lib/libdsplibs.so to reconfigure the DSCache maintenance task. The modified configuration replaces the legitimate maintenance script path with a Perl interpreter executing attacker-controlled code. When the maintenance task fires, the attacker's payload runs as root. Post-execution, the attacker restores the original DSCache configuration and library patches — a sophisticated anti-forensics technique. Note: This privilege escalation uses a *separate, still-unpatched vulnerability* — there is no vendor fix for this component as of October 16, 2026.
Conditions required:
  • Web-user code execution from step 3
  • dsinvoked service running (default on all ICS)
Where this breaks in practice:
  • The priv-esc vulnerability is unpatched but not publicly documented in detail — however, UTA0576 has demonstrated it works
Detection/coverage: Monitor for unexpected modifications to libdsplibs.so in memory. Alert on Perl processes spawned by dsinvoked. Check DSCache configuration integrity. Volexity notes that Ivanti's Integrity Checker Tool (ICT) may not detect this compromise.
STEP 05

Credential Harvest & Domain Pivot

With root access on the VPN appliance, the attacker extracts every credential flowing through the device. UTA0576 deployed custom tools: an LDAPv3 client (ml.py, rq5.py) to extract domain passwords, LAPS local admin passwords, and BitLocker recovery keys from Active Directory; a Kerberoast tool (mk.py) to request service tickets for offline cracking; a password spraying tool (sp_run.py) against AD; and a packet capture parser (h.py) extracting plaintext LDAP credentials, HTTP Basic Auth, and NTLM hashes from traffic transiting the appliance. All results were staged in /tmp/ as JSON files and exfiltrated over the existing HTTPS exploit channel — no C2 implant, no web shell, no separate backdoor.
Conditions required:
  • Root access from step 4
  • ICS appliance configured with LDAP/AD integration (standard enterprise deployment)
Where this breaks in practice:
  • None for credential extraction — the VPN appliance *must* have AD connectivity to function
  • No separate C2 channel needed — re-exploiting the chain for each command is noisy but effective
Detection/coverage: Monitor for anomalous LDAP queries originating from the ICS appliance (especially queries for ms-Mcs-AdmPwd, ms-FVE-RecoveryPassword). Alert on bulk AD object enumeration from the VPN gateway IP. Network segmentation between the ICS management plane and AD can limit blast radius.
03 · Compensating Control

1
CRITICAL 9.6→HIGH 7.2
SEVERITY REDUCED
Patch to 25.1.3.0+ / 25.1.1.2+ / 22.8R2.5+ immediately — This is a KEV-listed zero-day with active exploitation and a CISA deadline of today (October 16, 2026). Per the noisgate mitigation SLA for CRITICAL, deploy within 3 days — but given confirmed in-the-wild exploitation, this should be treated as an hours-level emergency. Apply the vendor patch during the next available maintenance window, even if that means tonight. Note: patching does NOT address the chained privilege-escalation vulnerability, so post-patch forensic triage is still required.
2
CRITICAL 9.6→MEDIUM 5.5
SEVERITY REDUCED
Block X-DSCGI-PATH header at WAF or reverse proxy — If you front your ICS appliance with a WAF, CDN, or reverse proxy (many orgs do not), add a rule to strip or reject any inbound request containing the X-DSCGI-PATH header. This breaks step 2 of the attack chain — the authentication bypass — without requiring an appliance-level patch. Deploy within hours as a stopgap if patching requires a maintenance window. Cloudflare, ModSecurity, and AWS WAF rules are in development per Previdian.
3
CRITICAL 9.6→HIGH 7.8
SEVERITY REDUCED
Restrict ICS management-plane network access to AD/LDAP — Segment the ICS appliance so it can only reach the LDAP/AD servers required for authentication — not the broader domain. Apply firewall rules blocking the ICS from querying arbitrary AD attributes (especially ms-Mcs-AdmPwd for LAPS and ms-FVE-RecoveryPassword for BitLocker). This does not prevent initial compromise but limits the blast radius of post-exploitation credential theft from domain-scale to appliance-local. Deploy within 3 days per the noisgate mitigation SLA.
4
CRITICAL 9.6→CRITICAL 9.6
Hunt for compromise using Volexity IOCs and forensic guidance — Even if you have patched, you must assume prior compromise given that exploitation began October 3 — before disclosure. Run Ivanti's Integrity Checker Tool (ICT) but do not rely on it alone — Volexity notes ICT may miss this compromise. Perform memory forensics looking for: files in /tmp/ (ml.py, rq5.py, mk.py, sp_run.py, h.py), hidden directories like /data/.icscap/, renamed tcpdump binaries, gaps in /home/runtime/dlogs/debuglog and /var/log/audit/audit.log. Check for anomalous LDAP queries from the ICS appliance IP. If compromise is confirmed, rotate all credentials accessible via LDAP from the appliance — including LAPS passwords and BitLocker recovery keys.
5
CRITICAL 9.6→HIGH 7.0
SEVERITY REDUCED
Force credential rotation for all LDAP-accessible accounts — If the ICS appliance has been compromised (or you cannot confirm it hasn't been), rotate domain passwords, LAPS local admin passwords, BitLocker recovery keys, and any service account credentials that transited the VPN. The attacker's documented playbook specifically targets these. Until rotation is complete, assume the attacker retains valid domain credentials even after patching. Deploy within 3 days.
What doesn't work
  • MFA on VPN user sessions — The bypass operates at the CGI routing layer, before the authentication/MFA stack is ever invoked. MFA protects legitimate user logins; it does not protect against X-DSCGI-PATH header spoofing to reach authenticated handlers without logging in at all.
  • Ivanti Integrity Checker Tool (ICT) as sole detection — Volexity explicitly notes that ICT may not detect this compromise. UTA0576's anti-forensic techniques (in-memory library patching, log tampering, DSCache restoration) were specifically designed to evade ICT. Use ICT as one signal among many, not as a clean bill of health.
  • IDS/IPS signature-only detection — Exploitation occurs over HTTPS (port 443). Unless you are performing TLS inspection on traffic to your VPN appliance (uncommon — it's the TLS termination point), network IDS cannot see the X-DSCGI-PATH header. Detection must happen on-appliance or via WAF in the TLS path.
  • IP-based blocking of known attacker IPs — Previdian observed 57 unique IPs in 48 hours across 5 countries. Attacker infrastructure is disposable (Evoxt VPS). Blocking known IOC IPs is useful for retroactive log hunting but provides negligible forward-looking protection.
04 · Intelligence Metadata

The supporting signals.

In-the-Wild ExploitationConfirmed and active. Threat actor UTA0576 (assessed criminal, not espionage) exploited this as a zero-day from October 3, 2026 — 10 days before the patch. Volexity documented the full attack chain. Previdian observed 118 exploitation attempts from 57 unique IPs across Germany, India, Japan, Singapore, and the US within 48 hours of disclosure.
CISA KEV StatusListed October 13, 2026 — same day as vendor disclosure. Remediation deadline set to October 16, 2026 (today). CISA alert.
EPSS Score0.25982 (26%) — this places CVE-2026-104935 in approximately the top 1–2% of all scored CVEs by exploitation probability. Extremely high signal.
Proof-of-Concept AvailabilityMultiple public PoCs. ApexBreach/CVE-2026-104935-Proof-of-concept (Python, full RCE). 0x45dd detection PoC on Sploitus (non-destructive bypass check). Nuclei template published October 14, 2026.
CVSS VectorCVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:L/A:L — Network/Low-complexity/No-auth/No-interaction. Vendor rated I and A as Low, which underrepresents the chained impact to root + credential theft.
Affected VersionsICS 25.1.1.1 and prior, 25.1.2.2 and prior, 22.8R2.4 and prior (Build 19097 was 'fully patched' at time of exploitation). Legacy branch 22.7R2.14 and earlier is NOT affected.
Fixed Versions25.1.3.0 (partial fix shipped July 2026), 25.1.3.1, 25.1.1.2, 22.8R2.5. Note: the chained privilege-escalation vulnerability remains unpatched — fix in development.
Internet Exposure~5,500 exposed appliances (Shadowserver, Oct 2026). ~27,000 instances (Censys). ICS appliances are internet-facing by design — they *are* the VPN gateway.
Researcher / DiscoveryDiscovered in-the-wild by Volexity during incident response initiated October 5, 2026. Attack attributed to UTA0576. Volexity assessed with medium confidence that UTA0576 used LLM-assisted exploit development. watchTowr Labs published independent analysis.
Chained VulnerabilitiesUTA0576 chained CVE-2026-104935 (auth bypass + RCE) with a separate, unpatched privilege-escalation vulnerability in the dsinvoked service to achieve root. The priv-esc CVE has not been publicly assigned. Full chain: unauth → web-user RCE → root → credential theft → domain pivot.

Sources.

  1. Volexity — Chained Exploitation of Ivanti Connect Secure Zero-Days (UTA0576)
  2. CISA — Adds Two Known Exploited Vulnerabilities to Catalog (Oct 13, 2026)
  3. Rapid7 — Ivanti Connect Secure Zero-Day Exploited in the Wild
  4. watchTowr Labs — Ivanti Connect Secure Authentication Bypass Analysis
  5. ApexBreach — CVE-2026-104935 Proof-of-Concept (GitHub)
  6. Previdian — CVE-2026-104935 Exploitation Observed (Sensor Data)
  7. GBHackers — Ivanti Connect Secure CVE-2026-104935 Flaw Enables Unauthenticated RCE
  8. Nuclei Template — CVE-2026-104935 Detection
05 · The Call

Final Verdict
↑ UPGRADED to CRITICAL (9.6/10)

Why this verdict

  • Zero friction in the attack chain: The vulnerability requires no authentication, no user interaction, has low attack complexity, and is reachable over the network. The public PoC is a one-liner: python3 exploit.py --url https://target --command whoami. There is no meaningful barrier between an attacker on the internet and code execution on the appliance.
  • Active zero-day exploitation with credential theft: UTA0576 exploited this for 10 days before any patch existed. The post-exploitation workflow — extracting LAPS passwords, BitLocker keys, Kerberoasting, password spraying — is a direct path to domain compromise. This is not theoretical; Volexity documented it happening.
  • Role multiplier: Ivanti Connect Secure is a network edge appliance from the high-value role catalog. By definition, 100% of ICS installs are internet-facing perimeter devices — that is the product's entire purpose. The blast radius is not host-level; it is domain-scale and fleet-scale. A compromised VPN gateway gives the attacker: (1) root on the appliance, (2) every credential transiting the VPN in real-time via packet capture, (3) LDAP access to AD including LAPS and BitLocker keys, (4) Kerberoast and password-spray capability against the domain. In enterprise deployments, ICS is often the *sole* remote-access path, making it identity-scale and network-scale simultaneously. Because this is a canonical high-value-role component (≥10% — indeed 100% — of installs occupy the high-value perimeter role) and the chain ends in domain takeover, the verdict floor is CRITICAL.
  • Public PoCs + Nuclei template lower the bar to commodity exploitation: Within 24 hours of disclosure, a working Python exploit and a Nuclei detection template were public. The EPSS of 0.26 (top ~1–2% of all CVEs) corroborates that mass exploitation is imminent or already underway. Previdian data shows 57 unique attacker IPs within 48 hours.
  • Vendor CVSS underrates impact: The vendor vector scores Integrity and Availability as Low (I:L/A:L), which reflects only the initial authentication bypass in isolation. The real-world chain — to root, to credential theft, to domain compromise — demands C:H/I:H/A:H with Scope Changed. The 8.6 HIGH rating gives defenders a false sense of scheduling flexibility on what is an emergency.

Why not higher?

A 9.6 is already near the ceiling. The only reason this is not a 10.0 is that the privilege-escalation chain to root relies on a second vulnerability (the unpatched dsinvoked bug), meaning the auth bypass alone delivers web-user RCE rather than immediate root. Additionally, the legacy 22.7R2.x branch is unaffected, which narrows the vulnerable population slightly. If/when the priv-esc component gets its own CVE and a public PoC, the combined chain approaches 10.0.

Why not lower?

Every downward pressure is absent. There are no prerequisites beyond internet reachability — which is guaranteed for a VPN appliance. There is no user interaction. Complexity is low. Public exploit code exists. Active exploitation is confirmed. The product is canonically a perimeter device, making 'requires network access' a vacuous condition rather than meaningful friction. The blast radius extends from the appliance to the domain. Dropping this below CRITICAL would require evidence that the exploit chain fails in practice — and Volexity's incident report proves the opposite.

06 · Verification

Crowdsourced verification payload.

Run from any auditor workstation with Python 3.6+ and network access to the target ICS appliance on port 443. No special privileges required. Invoke as: python3 check_cve_2026_104935.py https://vpn.company.com. The script performs a non-destructive version check only — it does NOT attempt exploitation.

noisgate-verify.py
PYTHONREAD-ONLYSAFE
#!/usr/bin/env python3
"""CVE-2026-104935 Vulnerability Checker for Ivanti Connect Secure.
Non-destructive version-based check. Does NOT attempt exploitation.
Exit codes: 1=VULNERABLE, 0=PATCHED, 2=UNKNOWN
"""
import sys
import ssl
import re
import urllib.request
import urllib.error

def get_ics_version(target_url):
    ctx = ssl.create_default_context()
    ctx.check_hostname = False
    ctx.verify_mode = ssl.CERT_NONE
    urls = [
        f"{target_url}/dana-na/auth/url_default/welcome.cgi",
        f"{target_url}/dana-na/auth/url_0/welcome.cgi",
        f"{target_url}/",
    ]
    for url in urls:
        try:
            req = urllib.request.Request(url, headers={"User-Agent": "noisgate-checker/1.0"})
            resp = urllib.request.urlopen(req, timeout=15, context=ctx)
            body = resp.read().decode("utf-8", errors="replace")
            hdrs = dict(resp.getheaders())
            for pat in [
                r'version["\s:=]+(\d+\.\d+(?:\.\d+)*(?:R\d+(?:\.\d+)*)?)',
                r'(25\.\d+\.\d+\.\d+)',
                r'(22\.\d+R\d+\.\d+)',
            ]:
                m = re.search(pat, body, re.IGNORECASE)
                if m:
                    return m.group(1)
            srv = hdrs.get("Server", "")
            if any(k in srv for k in ("ICS", "Ivanti", "Pulse")):
                m = re.search(r'(\d+\.\d+(?:\.\d+)*(?:R\d+(?:\.\d+)*)?)', srv)
                if m:
                    return m.group(1)
            if "welcome.cgi" in url and resp.status == 200:
                return "ICS_DETECTED_NO_VERSION"
        except (urllib.error.URLError, urllib.error.HTTPError, OSError):
            continue
    return None

def is_vulnerable(v):
    if v == "ICS_DETECTED_NO_VERSION":
        return None
    # 22.xRy.z
    m = re.match(r'22\.(\d+)R(\d+)\.(\d+)', v)
    if m:
        minor, rel, patch = int(m.group(1)), int(m.group(2)), int(m.group(3))
        if minor <= 7:
            return False          # 22.7 and below unaffected
        if minor == 8 and rel == 2:
            return patch <= 4     # 22.8R2.4 and prior vuln; .5 fixed
        return None
    # 25.x.y.z
    m = re.match(r'25\.(\d+)\.(\d+)\.(\d+)', v)
    if m:
        mi, mc, pa = int(m.group(1)), int(m.group(2)), int(m.group(3))
        if mi > 1:
            return False
        if mi == 1:
            if mc >= 3:
                return False      # 25.1.3.0+ fixed
            if mc == 1:
                return pa <= 1    # 25.1.1.1 vuln; .2 fixed
            if mc == 2:
                return pa <= 2    # 25.1.2.2 vuln
            return True
    return None

def main():
    if len(sys.argv) != 2:
        print(f"Usage: {sys.argv[0]} <https://ics-appliance-url>")
        print(f"Example: {sys.argv[0]} https://vpn.company.com")
        sys.exit(2)
    target = sys.argv[1].rstrip("/")
    if not target.startswith("http"):
        target = f"https://{target}"
    print(f"[*] CVE-2026-104935 Checker - Ivanti Connect Secure")
    print(f"[*] Target: {target}")
    print()
    ver = get_ics_version(target)
    if ver is None:
        print("[?] Could not detect Ivanti Connect Secure on target.")
        print("UNKNOWN")
        sys.exit(2)
    if ver == "ICS_DETECTED_NO_VERSION":
        print("[!] ICS detected but version not extractable.")
        print("[!] Check manually against fixed: 25.1.3.0, 25.1.1.2, 22.8R2.5")
        print("UNKNOWN")
        sys.exit(2)
    print(f"[+] Detected version: {ver}")
    result = is_vulnerable(ver)
    if result is True:
        print(f"[!] {ver} is VULNERABLE to CVE-2026-104935")
        print("[!] Upgrade immediately to 25.1.3.0+ / 25.1.1.2+ / 22.8R2.5+")
        print("VULNERABLE")
        sys.exit(1)
    elif result is False:
        print(f"[+] {ver} is PATCHED for CVE-2026-104935")
        print("PATCHED")
        sys.exit(0)
    else:
        print(f"[?] {ver} not matched to known ranges.")
        print("[?] Fixed: 25.1.3.0, 25.1.1.2, 22.8R2.5")
        print("UNKNOWN")
        sys.exit(2)

if __name__ == "__main__":
    main()