A Brief Look at FortiJump (FortiManager CVE-2024-47575)

IoT 2天前 admin
70 0 0

CVE-2024-47575, also known as FortiJump, recently gained widespread attention after news of in-the-wild exploitation leaked prior to any security advisory. According to Mandiant, a threat actor has been exploiting this vulnerability since at least June 2024. Given the highly sensitive nature of centralized management devices, we decided to take a deeper look.
CVE-2024-47575,也称为 FortiJump,最近在任何安全公告之前泄露了在野利用的消息后,它引起了广泛关注。根据 Mandiant 的说法,至少从 2024 年 6 月开始,威胁行为者就一直在利用这个漏洞。鉴于集中管理设备的高度敏感性,我们决定进行更深入的研究。

Connecting a Device to FortiManager
将设备连接到 FortiManager

The advisory describes CVE-2024-47575 as “missing authentication in fgfmsd”. Combined with the list of IoCs and mitigations, we can assume the first step of the exploit is registering a device.
该公告将 CVE-2024-47575 描述为“fgfmsd 中缺少身份验证”。结合 IoC 和缓解措施的列表,我们可以假设漏洞利用的第一步是注册设备。

We started by setting up a lab environment with a FortiManager VM and a FortiGate firewall, then configured the FortiGate firewall to connect to the FortiManager instance:
我们首先设置一个包含 FortiManager VM 和 FortiGate 防火墙的实验室环境,然后配置 FortiGate 防火墙以连接到 FortiManager 实例:

config system central-management
    set type fortimanager
    set fmg “192.168.250.103”
end

We were able to confirm that the device shows up in our FortiManager
我们能够确认该设备显示在我们的 FortiManager 中

# 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

From our understanding of the vulnerability, getting a device in this unregistered state should be the first step to exploiting CVE-2024-47575. The next step is to replicate the requests in Python.
根据我们对漏洞的理解,让设备处于这种未注册状态应该是利用 CVE-2024-47575 的第一步。下一步是在 Python 中复制请求。

We know the FortiGate to FortiManager (FGFM) protocol uses TLS on TCP port 541, so we started a TLS server and configured our FortiGate firewall to use that as its FortiManager IP. Unfortunately, the FortiGate refused to connect
我们知道 FortiGate 到 FortiManager (FGFM) 协议在 TCP 端口 541 上使用 TLS,因此我们启动了一个 TLS 服务器并配置了我们的 FortiGate 防火墙以将其用作其 FortiManager IP。不幸的是,FortiGate 拒绝连接

# 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

After examining logs on our FortiGate, we realized it was rejecting the auto-generated server certificate. Luckily, during prior research for CVE-2024-23113, we discovered that the FGFM client accepts the Fortinet factory certificate that is included on all FortiGate VMs (/data/etc/cert/local/root_Fortinet_Factory.cer). The private key for this certificate is encrypted with a static AES key that can be extracted from the init binary. This time the FortiGate doesn’t immediately close the connection when it connects
在检查了 FortiGate 上的日志后,我们意识到它拒绝了自动生成的服务器证书。幸运的是,在之前对 CVE-2024-23113 的研究中,我们发现 FGFM 客户端接受所有 FortiGate 虚拟机上都包含的 Fortinet 工厂证书 ( /data/etc/cert/local/root_Fortinet_Factory.cer )。此证书的私有密钥使用静态 AES 密钥进行加密,该密钥可从 init 二进制文件中提取。这一次,FortiGate 在连接时不会立即关闭连接

#  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

As described by Watchtowr (and shown in less detail in Phrack), FGFM messages begin with an 8 byte header consisting of a magic number (\x36\xe0\x11\x00) and a size field. Each line ends with a CRLF, and the last line is empty and ends with a null byte. The first line is a request method/action, and the rest of the lines are key/value pairs. With this knowledge of the protocol, let’s write a Python script to send this data to FortiManager.
正如 Watchtowr 所描述的(在 Phrack 中显示得不太详细),FGFM 消息以一个 8 字节的报头开头,该报头由一个幻数 (\x36\xe0\x11\x00) 和一个大小字段组成。每行以 CRLF 结尾,最后一行为空,以 null 字节结尾。第一行是请求方法/操作,其余行是键/值对。了解了协议后,让我们编写一个 Python 脚本来将此数据发送到 FortiManager。

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)

Upon running this, we’re met with an exception:
运行此命令时,我们遇到了一个异常:

$ 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)

The server is requesting a client certificate. Let’s try that factory certificate from earlier.
服务器正在请求客户端证书。让我们试试前面的工厂证书。

+ context.load_cert_chain(certfile="root_Fortinet_Factory.cer ",keyfile="root_Fortinet_Factory.key")

$ python3 test-47575.py
b‘’

This time we got an empty response, so at least it looks like our certificate was accepted. Despite that, when we looked at the FortiManager logs, we noticed that the request was still being rejected
这次我们得到一个空响应,所以至少看起来我们的证书被接受了。尽管如此,当我们查看 FortiManager 日志时,我们注意到该请求仍然被拒绝

__get_handler: SNs don’t match <FortiGate> < FGTXXXXXXXXXXXXX > 

We realized the first value (“FortiGate”) was pulled from the client certificate common name, and the second value was the serialno parameter in the request. After changing the serialno parameter to “FortiGate”, we were met with a new error message:
我们意识到第一个值 (“FortiGate”) 是从客户端证书公用名中提取的,第二个值是请求中的 serialno 参数。将 serialno 参数更改为 “FortiGate” 后,我们遇到了一条新的错误消息:

Serial number does not match device model

The serial number begins with a prefix that identifies the product. Unfortunately, “FortiGate” doesn’t match the prefix of any valid device, which means we couldn’t use this certificate after all. That said, we already happened to have a device certificate that we extracted from a FortiGate appliance during prior research.
序列号以标识产品的前缀开头。不幸的是,“FortiGate”与任何有效设备的前缀都不匹配,这意味着我们根本无法使用此证书。也就是说,我们已经碰巧有一个设备证书,这是我们在之前的研究中从 FortiGate 设备中提取的。

Accidentally Extracting Device Certificates, The Hard Way
意外提取设备证书,困难的方法

We typically perform our research on FortiGate VM appliances out of convenience, but many companies use hardware appliances. As a result, we purchased a secondhand FortiGate 60E for research purposes last year. Our main goal was to identify any important differences between hardware and VM appliances, and part of that included looking at FortiGate’s modified U-Boot bootloader.
我们通常出于方便而对 FortiGate VM 设备进行研究,但许多公司都使用硬件设备。因此,我们去年购买了一台二手 FortiGate 60E 用于研究目的。我们的主要目标是确定硬件和 VM 设备之间的任何重要差异,其中一部分包括查看 FortiGate 修改后的 U-Boot 引导加载程序。

The FG60E appliance has two storage locations. The main storage is an 8GB EMMC chip, which stores the FortiOS system and configuration data. The second location is a 2MiB SPI flash chip. This flash chip stores the bootloader as well as some additional identifiers that are persistent across factory resets.
FG60E 设备有两个存储位置。主存储是一个 8GB 的 EMMC 芯片,用于存储 FortiOS 系统和配置数据。第二个位置是一个 2MiB 的 SPI flash 芯片。此 flash 芯片存储 bootloader 以及一些在出厂重置后保持不变的其他标识符。

A Brief Look at FortiJump (FortiManager CVE-2024-47575)
Figure 1: EMMC (bottom) and SPI flash (top) inside of our FG60-E
图 1:FG60-E 内部的 EMMC(底部)和 SPI 闪存(顶部)

To dump the bootloader, we desoldered the SPI flash chip and connected it to a cheap TL866II universal programmer. The output was a raw dump of the flash contents, and our first step in analyzing this file was to run binwalk.
为了转储 bootloader,我们将 SPI flash 芯片拆焊并将其连接到廉价的 TL866II 通用编程器。输出是 Flash 内容的原始转储,我们分析此文件的第一步是运行 binwalk

$ 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 identified a “PEM RSA private key” as well as several certificates. After extracting the certificate and private key at the very end of the flash dump, we realized that this was the device certificate and associated private key. Although we didn’t have any use for this certificate at the time, the fact that we already had this certificate from prior research saved us a lot of time when analyzing CVE-2024-47575.
Binwalk 发现了一个“PEM RSA 私钥”以及多个证书。在 Flash 转储的末尾提取证书和私钥后,我们意识到这是设备证书和关联的私钥。虽然我们当时没有用这个证书,但事实上我们已经从之前的研究中获得了这个证书,这在分析 CVE-2024-47575 时为我们节省了大量时间。

After plugging the FG60E device certificate and key into our script, we receive a response and see our device in the FortiManager device list.
将 FG60E 设备证书和密钥插入脚本后,我们会收到响应,并在 FortiManager 设备列表中看到我们的设备。

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'

Firmware Decryption 固件解密

Now that we can talk to the FortiManager intance via FGFM, we need to know what to tell it. The advisory doesn’t provide many hints, so we turned to patch diffing. Unfortunately, we immediately hit a snag since, much like FortiGate devices, the FortiManager rootfs files are encrypted. Even worse, the firmware encryption seemed to be completely different from FortiGate firmware encryption.
现在我们可以通过 FGFM 与 FortiManager 插件交谈,我们需要知道该如何告诉它。该公告没有提供很多提示,因此我们转向了补丁差异。不幸的是,我们立即遇到了障碍,因为与 FortiGate 设备非常相似,FortiManager rootfs 文件是加密的。更糟糕的是,固件加密似乎与 FortiGate 固件加密完全不同。

We began by unpacking the firmware update file, which initially looks similar to FortiGate. It contains a rootfs.gz and a vmlinuz, which we expect to correspond to the initramfs and kernel image. As noted, the rootfs is encrypted, so we planned to start our analysis with the kernel image. We tried to use vmlinux-to-elf to convert the kernel image into a format that we could load into Ghidra, but it failed to find the kernel version and symbol table.
我们首先解压缩固件更新文件,它最初看起来类似于 FortiGate。它包含一个 rootfs.gz 和一个 vmlinuz,我们希望它们对应于 initramfs 和内核映像。如前所述,rootfs 是加密的,因此我们计划从内核映像开始分析。我们尝试使用 vmlinux-to-elf 将内核镜像转换为可以加载到 Ghidra 中的格式,但找不到内核版本和符号表。

We quickly realized that not only was the rootfs encrypted, but the kernel itself was also obfuscated. From a hexdump, we could clearly see enough structure to indicate that parts of the file were unencrypted, and we could even see some strings related to decompressing the gzip-compressed kernel image. Despite seeing these strings, we didn’t see any gzip header in the binary. This led us down the path of reverse engineering the stub decompressor and finding the custom code responsible for the obfuscation.
我们很快意识到,不仅 rootfs 被加密,而且内核本身也被混淆了。从 hexdump 中,我们可以清楚地看到足够的结构来表明文件的某些部分是未加密的,我们甚至可以看到一些与解压缩 gzip 压缩的内核镜像相关的字符串。尽管看到了这些字符串,但我们在二进制文件中没有看到任何 gzip 标头。这引导我们走上了对存根解压缩器进行逆向工程并找到负责混淆的自定义代码的道路。

Before decompressing the kernel, the decompression function XORs a large range of memory with a 32-byte key. We wrote a script to XOR this data in-place, and after doing so we were able to successfully run vmlinux-to-elf and load a deobfuscated kernel image into Ghidra.
在解压缩内核之前,解压缩函数使用 32 字节的密钥对大范围的内存进行 XOR 运算。我们编写了一个脚本来就地对这些数据进行 XOR 运算,之后我们能够成功运行 vmlinux-to-elf 并将反混淆的内核映像加载到 Ghidra 中。

Drawing on experience from breaking FortiGate encryption, we traced the functions responsible for loading the initramfs and were able to find a custom function that loads a static AES-CTR key and IV. We extracted these values and were able to successfully decrypt the rootfs images, which finally gave us access to the firmware running on the FortiManager.
鉴破解 FortiGate 加密的经验,我们追踪了负责加载 initramfs 的函数,并能够找到加载静态 AES-CTR 密钥和 IV 的自定义函数。我们提取了这些值,并能够成功解密 rootfs 映像,这最终使我们能够访问在 FortiManager 上运行的固件。

Patch Diffing 补丁比较

Unlike FortiGate, where nearly all functionality is combined into the /bin/init binary, FortiManager is organized more like a typical Linux system with many binaries, libraries, and scripts. We used pkgdiff to narrow these files down, and we found 5 libraries and 10 binaries had changed between versions 7.6.0 and 7.6.1. Our eyes were immediately drawn to fgfmsd, which is the service mentioned in the advisory, but we didn’t find anything that looked like a vulnerability fix in the dozens of changed functions. This led us to look at other changed components, including the shared libraries. libdmserver.so contained what looks like a fix for a command injection vulnerability
与 FortiGate 不同,FortiGate 几乎所有功能都组合到 /bin/init 二进制文件中,而 FortiManager 的组织方式更像一个典型的 Linux 系统,具有许多二进制文件、库和脚本。我们使用 pkgdiff 来缩小这些文件的范围,我们发现 5 个库和 10 个二进制文件在版本 7.6.0 和 7.6.1 之间发生了变化。我们的目光立即被 fgfmsd 所吸引,这是公告中提到的服务,但我们在数十个更改的功能中没有找到任何看起来像漏洞修复的东西。这让我们开始查看其他更改的组件,包括共享库。libdmserver.so 包含看起来像是命令注入漏洞的修复程序

A Brief Look at FortiJump (FortiManager CVE-2024-47575)
Figure 2: Call to system() with externally controlled contents was removed
图 2:删除了对具有外部控制内容的 system() 的调用

The patched function seemed to be called by a handler for an “rcs/checkout” operation, and the function seemed to be checking out a database from an internal RCS repository. This RCS repository seems to be used for configuration management, and the database corresponds to a device configuration. We suspect that an exploit would involve sending some sort of FGFM request for a device configuration, and that a parameter in that request would be passed to the /bin/cp command. Unfortunately, we were unable to identify a request or parameter that could be used to exploit this vulnerability.
修补后的函数似乎由处理程序调用以执行 “rcs/checkout” 操作,并且该函数似乎正在从内部 RCS 存储库中签出数据库。此 RCS 存储库似乎用于配置管理,数据库对应于设备配置。我们怀疑漏洞利用会涉及发送某种针对设备配置的 FGFM 请求,并且该请求中的参数将传递给 /bin/cp 命令。遗憾的是,我们无法识别可用于利用此漏洞的请求或参数。

Conclusion 结论

Although we weren’t able to create a full proof-of-concept exploit, CVE-2024-47575 appears to be a command injection vulnerability. Command injections are a very highly exploitable class of bug since the same payload can generally be used for all vulnerable devices and versions. As such, we recommend patching as soon as possible. Whether patched or not, we also recommend restricting access to the FGFM port as much as possible by following the mitigation steps in the CVE-2024-47575 advisory. Given that this is the third vulnerability related to the FGFM, it seems unwise to leave the service publicly accessible on the internet.
虽然我们无法创建完整的概念验证漏洞,但 CVE-2024-47575 似乎是一个命令注入漏洞。命令注入是一类非常容易利用的错误,因为相同的有效负载通常可用于所有易受攻击的设备和版本。因此,我们建议尽快进行修补。无论是否修补,我们还建议按照 CVE-2024-47575 公告中的缓解步骤,尽可能限制对 FGFM 端口的访问。鉴于这是与 FGFM 相关的第三个漏洞,因此在 Internet 上公开访问该服务似乎是不明智的。

原文始发于Bishop Fox, Security Experts:A Brief Look at FortiJump (FortiManager CVE-2024-47575)

版权声明:admin 发表于 2024年11月3日 下午12:49。
转载请注明:A Brief Look at FortiJump (FortiManager CVE-2024-47575) | CTF导航

相关文章