深入解析FortiJump(FortiManager CVE-2024-47575)
CVE-2024-47575(又称FortiJump)在安全公告发布前因野外利用消息泄露而受到广泛关注。据Mandiant报告,至少自2024年6月起已有威胁攻击者利用此漏洞。鉴于集中管理设备的高度敏感性,我们决定深入分析。
连接设备到FortiManager
公告将CVE-2024-47575描述为“fgfmsd中的身份验证缺失”。结合IoC列表和缓解措施,我们推测漏洞利用的第一步是注册设备。
我们首先搭建了实验室环境,包含FortiManager VM和FortiGate防火墙,并配置FortiGate连接到FortiManager实例:
1
2
3
4
|
config system central-management
set type fortimanager
set fmg "192.168.250.103"
end
|
确认设备出现在FortiManager中:
1
2
3
4
5
6
7
|
# diagnose dvm device list
--- There are currently 1 devices/vdoms managed ---
--- There are currently 1 devices/vdoms count for license ---
TYPE OID SN HA IP NAME ADOM IPS FIRMWARE HW_GenX
unregistered 166 FGTXXXXXXXXXXXXX - 192.168.250.124
FGTXXXXXXXXXXXXX root N/A 7.0 MR2 (1396) N/A
|
根据漏洞理解,使设备处于未注册状态是利用CVE-2024-47575的第一步。下一步是用Python复现请求。
FGFM协议通信
FortiGate到FortiManager(FGFM)协议使用TCP端口541上的TLS。我们启动TLS服务器并配置FortiGate使用该IP作为FortiManager地址,但FortiGate拒绝连接:
1
2
3
4
5
6
7
8
9
|
# ncat --ssl -nlvp 541
Ncat: Version 7.80 ( https://nmap.org/ncat )
Ncat: Generating a temporary 2048-bit RSA key. Use --ssl-key and --ssl-cert to use a permanent one.
Ncat: SHA-1 fingerprint: F74F 629E 3757 3845 EC74 7EA6 ED47 B899 598A 2364
Ncat: Listening on :::541
Ncat: Listening on 0.0.0.0:541
Ncat: Connection from 192.168.250.124.
Ncat: Connection from 192.168.250.124:8394.
NCAT DEBUG: SSL_read error on 5: error:00000005:lib(0):func(0):DH lib
|
检查FortiGate日志后发现其拒绝了自动生成的服务器证书。幸运的是,在之前研究CVE-2024-23113时,我们发现FGFM客户端接受包含在所有FortiGate VM中的Fortinet工厂证书(/data/etc/cert/local/root_Fortinet_Factory.cer)。该证书的私钥使用静态AES密钥加密,可从init二进制文件中提取。此次FortiGate连接后未立即关闭连接:
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
|
# ncat --ssl --ssl-cert root_Fortinet_Factory.cer --ssl-key root_Fortinet_Factory.key -nlvp 541
Ncat: Version 7.80 ( https://nmap.org/ncat )
Ncat: Listening on :::541
Ncat: Listening on 0.0.0.0:541
Ncat: Connection from 192.168.250.124.
Ncat: Connection from 192.168.250.124:11554.
6get auth
serialno=FGTXXXXXXXXXXXXX
mgmtid=00000000-0000-0000-0000-000000000000
platform=FortiGate-60E
fos_ver=700
minor=2
patch=4
build=1396
branch=1396
maxvdom=2
fg_ip=192.168.250.124
hostname=FGTXXXXXXXXXXXXX
harddisk=yes
biover=04000002
harddisk_size=30720
logdisk_size=30107
mgmt_mode=normal
enc_flags=0
mgmtip=192.168.250.124
mgmtport=443
|
FGFM消息格式
根据Watchtowr描述(Phrack中也有简要提及),FGFM消息以8字节头部开始,包含魔术数字(\x36\xe0\x11\x00)和大小字段。每行以CRLF结束,最后一行为空并以空字节结束。第一行是请求方法/动作,其余行是键值对。基于此协议知识,我们编写Python脚本发送数据到FortiManager。
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
|
import socket, struct, ssl
request=b"""get auth
serialno=FGTXXXXXXXXXXXXX
mgmtid=00000000-0000-0000-0000-000000000000
platform=FortiGate-60E
fos_ver=700
minor=2
patch=4
build=1396
branch=1396
maxvdom=2
fg_ip=192.168.250.124
hostname=FGTXXXXXXXXXXXXX
harddisk=yes
biover=04000002
harddisk_size=30720
logdisk_size=30107
mgmt_mode=normal
enc_flags=0
mgmtip=192.168.250.124
mgmtport=443
\0""".replace(b"\n",b"\r\n")
def sendmsg(socket, request):
message=struct.pack(">II", 0x36e01100, len(request)+8)+request
socket.send(message)
hdr=socket.read(8)
if len(hdr)!=8:
return hdr
magic, size=struct.unpack(">II", socket.read(8))
return socket.read(size)
host=("192.168.250.103","541")
context=ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
context.check_hostname=False
context.verify_mode=ssl.CERT_NONE
s=socket.create_connection(host, 3)
ssl_sock=context.wrap_socket(s)
response=sendmsg(ssl_sock, request)
print(response)
|
运行后出现异常:
1
2
3
4
5
6
7
8
9
|
$ python3 test-47575.py
Traceback (most recent call last):
File "test-47575.py", line 41, in <module>
response=sendmsg(ssl_sock, request)
File "test-47575.py", line 30, in sendmsg
magic, size=struct.unpack(">II", socket.read(8))
File "/usr/lib/python3.8/ssl.py", line 1130, in read
return self._sslobj.read(len)
ssl.SSLError: [SSL: TLSV13_ALERT_CERTIFICATE_REQUIRED] tlsv13 alert certificate required (_ssl.c:2649)
|
服务器请求客户端证书。尝试使用之前的工厂证书:
1
|
context.load_cert_chain(certfile="root_Fortinet_Factory.cer ",keyfile="root_Fortinet_Factory.key")
|
1
2
|
$ python3 test-47575.py
b‘’
|
此次得到空响应,表明证书被接受。但查看FortiManager日志时发现请求仍被拒绝:
1
|
__get_handler: SNs don’t match <FortiGate> < FGTXXXXXXXXXXXXX >
|
第一个值(“FortiGate”)来自客户端证书通用名称,第二个值是请求中的serialno参数。将serialno参数改为“FortiGate”后,出现新错误消息:
1
|
Serial number does not match device model
|
序列号以产品标识前缀开头,“FortiGate”不匹配任何有效设备前缀,因此无法使用此证书。但我们在之前研究中已从FortiGate设备提取了设备证书。
意外提取设备证书
我们通常为方便起见在FortiGate VM设备上进行研究,但许多公司使用硬件设备。因此,去年我们购买了二手FortiGate 60E用于研究。主要目标是识别硬件和VM设备之间的重要差异,包括查看FortiGate修改的U-Boot引导加载程序。
FG60E设备有两个存储位置。主存储是8GB EMMC芯片,存储FortiOS系统和配置数据。第二个位置是2MiB SPI闪存芯片,存储引导加载程序以及一些在工厂重置后持久的附加标识符。
为了转储引导加载程序,我们拆焊SPI闪存芯片并连接到便宜的TL866II通用编程器。输出是闪存内容的原始转储,分析该文件的第一步是运行binwalk。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
$ binwalk spi.bin
DECIMAL HEXADECIMAL DESCRIPTION
--------------------------------------------------------------------------------
280184 0x44678 CRC32 polynomial table, little endian
325409 0x4F721 Certificate in DER format (x509 v3), header length: 4, sequence length: 76
325413 0x4F725 Certificate in DER format (x509 v3), header length: 4, sequence length: 144
325461 0x4F755 Certificate in DER format (x509 v3), header length: 4, sequence length: 184
325509 0x4F785 Certificate in DER format (x509 v3), header length: 4, sequence length: 176
325557 0x4F7B5 Certificate in DER format (x509 v3), header length: 4, sequence length: 168
325601 0x4F7E1 Certificate in DER format (x509 v3), header length: 4, sequence length: 92
325605 0x4F7E5 Certificate in DER format (x509 v3), header length: 4, sequence length: 160
325649 0x4F811 Certificate in DER format (x509 v3), header length: 4, sequence length: 84
325653 0x4F815 Certificate in DER format (x509 v3), header length: 4, sequence length: 152
2088962 0x1FE002 PEM certificate
2093584 0x1FF210 PEM certificate
2095106 0x1FF802 PEM RSA private key
|
Binwalk识别出“PEM RSA私钥”和多个证书。提取闪存转储末尾的证书和私钥后,我们意识到这是设备证书和相关私钥。虽然当时此证书无用,但之前研究已有此证书的事实为分析CVE-2024-47575节省了大量时间。
将FG60E设备证书和密钥插入脚本后,我们收到响应并在FortiManager设备列表中看到设备:
1
|
b'0\r\nrequest=auth\r\nserialno=FMG-VM0000000000\r\nuser=\r\npasswd=\r\nmgmtport=443\r\nkeepalive_interval=120\r\nchan_window_sz=32768\r\nsock_timeout=360\r\nmgmtid=3939167a-975d-51df-4d9a-046004f6d298\r\n\r\n\x00'
|
固件解密
现在可以通过FGFM与FortiManager实例通信,但需要知道发送什么内容。公告未提供太多提示,因此我们转向补丁对比。不幸的是,我们立即遇到障碍,因为与FortiGate设备类似,FortiManager rootfs文件被加密。更糟糕的是,固件加密似乎与FortiGate固件加密完全不同。
我们首先解包固件更新文件,初始看起来与FortiGate相似。它包含rootfs.gz和vmlinuz,我们期望它们对应initramfs和内核映像。如前所述,rootfs被加密,因此我们计划从内核映像开始分析。尝试使用vmlinux-to-elf将内核映像转换为可加载到Ghidra的格式,但未能找到内核版本和符号表。
我们很快意识到不仅rootfs被加密,内核本身也被混淆。从hexdump中,我们可以清楚地看到足够结构表明文件部分未加密,甚至可以看到与解压缩gzip压缩内核映像相关的字符串。尽管看到这些字符串,但未在二进制中看到gzip头部。这引导我们逆向存根解压缩器并找到负责混淆的自定义代码。
在解压缩内核前,解压缩函数使用32字节密钥对大量内存范围进行XOR。我们编写脚本在适当位置XOR此数据,之后成功运行vmlinux-to-elf并将去混淆内核映像加载到Ghidra。
借鉴破解FortiGate加密的经验,我们跟踪负责加载initramfs的函数,并找到加载静态AES-CTR密钥和IV的自定义函数。提取这些值后,我们成功解密rootfs映像,最终获得对FortiManager上运行的固件的访问权限。
补丁对比
与FortiGate几乎所有功能合并到/bin/init二进制文件不同,FortiManager组织更像典型Linux系统,包含许多二进制文件、库和脚本。我们使用pkgdiff缩小这些文件范围,发现版本7.6.0和7.6.1之间有5个库和10个二进制文件更改。我们立即注意到fgfmsd,这是公告中提到的服务,但未在数十个更改函数中找到任何看起来像漏洞修复的内容。这引导我们查看其他更改组件,包括共享库。
libdmserver.so包含看似命令注入漏洞修复的内容:
修补后的函数似乎由“rcs/checkout”操作的处理程序调用,该函数似乎从内部RCS存储库检出数据库。此RCS存储库似乎用于配置管理,数据库对应设备配置。我们推测漏洞利用涉及发送某种FGFM请求以获取设备配置,并且该请求中的参数将传递给/bin/cp命令。不幸的是,我们未能识别可用于利用此漏洞的请求或参数。
结论
尽管未能创建完整的概念验证利用,但CVE-2024-47575似乎是命令注入漏洞。命令注入是非常高度可利用的漏洞类别,因为相同有效载荷通常可用于所有易受攻击的设备和版本。因此,我们建议尽快修补。无论是否修补,我们还建议通过遵循CVE-2024-47575公告中的缓解步骤尽可能限制对FGFM端口的访问。鉴于这是与FGFM相关的第三个漏洞,将服务公开在互联网上似乎不明智。
一如既往,Bishop Fox利用此研究扫描并提醒受影响的Cosmos客户。