天天看点

Fortigate防火墙设备存身远程代码执行高危漏洞

作者:虫虫安全

最近Fortigate设备暴露出一个非常严重(9.8分)的远程执行漏洞。设备商Fortinet已于上个月8日悄悄发布了更新,修补了漏洞,随即安全公告中披露了该漏洞,称该漏洞可能已被用于有针对性的攻击。

Fortigate防火墙设备存身远程代码执行高危漏洞

近日,安全人员发布的一项研究显示,在互联网公网上仍然暴露了愈336,000台设备仍然容易受到该漏洞的影响,这些设备还未安装官方补丁。本文虫虫就来分析一下该漏洞的的详细分析、利用和缓解方法,供大家学习参考。

概述

CVE-2023-27997是FortigateVPN远程代码执行,该VPN包含在其硬件防火墙中。 该漏洞源于堆溢出错误,严重程度为9.8分(满分10分)。研究人员表示,尽管补丁的严重性和可用性,但管理员修复它的速度很慢。

上周五Bishop Fox援引Shodan搜索引擎查询中检索到的数据表示,在互联网上暴露的489,337台设备中,有335,923台(即69%)还未修补补丁。甚至,有些设备,自从2015以来就没有从未更新过固件。Bishop Fox的漏洞验证脚本显示:

Fortigate防火墙设备存身远程代码执行高危漏洞

屏幕截图显示了漏洞的概念验证,程序利用堆溢出损坏并注入恶意代码,连接到攻击者控制的服务器,下载类Unix操作系统的BusyBox实用程序,并打开一个交互式shell,该shell允许易受攻击的计算机远程发出命令。该漏洞仅需要大约一秒钟即可完成。

漏洞细节

漏洞位于允许用户对VPN进行身份验证的Web界面。界面在设计上是面向互联网的,其接口为/remote/hostcheck_validate,,通过GET或 POST,可以发送一个名为enc参数

enc参数是一个包含种子、大小(2 字节)和数据的结构。大小和数据均已加密。

Fortigate防火墙设备存身远程代码执行高危漏洞

种子存储为8个十六进制字符,用于计算XOR密钥流的第一个状态:

Fortigate防火墙设备存身远程代码执行高危漏洞

sa是服务器创建的随机值,可以通过向接口/remote/info发出GET请求来检索该值。

密钥流的其他状态计算如下:

Fortigate防火墙设备存身远程代码执行高危漏洞
Fortigate防火墙设备存身远程代码执行高危漏洞

密钥流S可以与其余部分进行异或enc有效负载、大小和密文,以解密它们。它以十六进制字符串发送。

解密方法如下(简化代码):

Fortigate防火墙设备存身远程代码执行高危漏洞
Fortigate防火墙设备存身远程代码执行高危漏洞

函数parse_enc_data的行为如下:

计算MD5(16 字节),这是来自盐和种子的密钥的第一个状态(前 8 个字符) in)

分配大小的缓冲区in_len/2 + 1, out,以及十六进制解码输入到其中

计算用户给出的长度, given_len,通过将有效负载的前两个字节与密钥的前两个字节进行异或。

边界检查:验证给定的长度不大于缓冲区的大小。

就地解密整个字符串:异或前 14 个字节,然后计算新状态 K 1 ,用它对接下来的 16 个字节进行异或,然后重复。

在解密数据的末尾放置一个NULL字节。

将解密的值添加到包含HTTP输入参数的哈希图中。

漏洞位于当程序检查给定长度不大于发送的有效负载的长度时,它会比较 in_len和given_len。但是,虽然前者以十六进制描述有效负载的长度(例如 '41424343'),后者以原始字节描述其大小(例如 'ABCD')。 因此,given_len可以是应有的两倍。

这个错误使的不仅可以将解密过程应用于out,而且也为了之后的记忆。这导致了一个有趣的错误:不只可以覆盖堆中的字节,而是使用一些MD5对它们进行异或!

理论验证

该错误允许分配任意大小的块N,然后将缓冲区后的字节与MD5哈希值的密钥流进行XOR为此部分控制密钥。控制分配的缓冲区的大小以及异或溢出的大小。 此外,溢出的最后一个字节被清空。

一个想法

首先可以想到的是对某个地址的LSB进行异或以改变其位置,类似如此:

Fortigate防火墙设备存身远程代码执行高危漏洞

然而,该计算比较昂贵的:需要找到一个以306300000000。开头的MD5哈希值。 该散列是另一个散列的散列,这是另一个散列的散列,等等。例如,如果散列到要修改的指针的距离是0x1000,则这将是密钥流的第256个状态。一次计算并不困难,但暴力破解......

即使设法拿到了这样的散列,该修改后的指针之前的数据也会出现乱码,因为它会与之前的散列进行异或,无法选择其内容。

jemalloc分配器

由于听起来很难让任何东西正常工作,因此浏览底层分配器,看看是否有任何方法可以利用该错误。

底层堆jemalloc:

堆元数据独立存储; 可以安全地从一个块(区域)溢出到另一个块(区域)

可以轻松获得连续的分配(填补漏洞后)

对于相同大小的分配有某种LIFO机制:释放一块大小N并分配相同的大小会产生相同的指针。

最后一点实际上使利用变得更加容易:可以分配溢出的缓冲区,out,在同一地址,重复。

强大的原语

一旦触发该错误,它就会看起来像是一个糟糕的原语。然而,由于可以一致地分配out在内存中的同一位置进行多次缓冲,原语变得非常好。事实上,知道对一个值应用两次相同的XOR会使其保持不变:

Fortigate防火墙设备存身远程代码执行高危漏洞

如果使用完全相同的参数(种子和长度)触发错误两次,并且如果两次ou缓冲区被分配在相同的内存地址,溢出中的每个字节都会与相同的值进行异或两次。 除了一个字节被设置为NULL之外,内存完全没有改变。一项改进:一种将字节设置为零的方法,没有副作用。

此外,使用相同的种子触发错误两次,只有第一次将溢出长度设置为L,第二次到L+1,产生更好的结果。

在第一次迭代中,会完全搞乱数据,直到偏移处的字节L,将为NULL。在第二次迭代中,每个字节都会与密钥进行异或两次,从而再次变成原来的自己,除了Bl,这将取值KL ,作为0⊕ KL= KL 。改变L+1将变为NULL。

Fortigate防火墙设备存身远程代码执行高危漏洞

所以,给字节L任意值X,可以计算一个密钥流,这样KL = X ,并应用原语两次,一次使用长度L,一次长度L + 1。

这是一个例子:我们要设置B[5000] 到0X50。计算一个种子,使得K[5000] = 0X50。这是8 th字节250th 状态,所以S250 [8] = 0X50。 例如,如果盐是e0b638ac ,可以使用种子00690000获取:

Fortigate防火墙设备存身远程代码执行高危漏洞

然后应用长度为4999的原语。我们得到以下结果:

Fortigate防火墙设备存身远程代码执行高危漏洞

第二次应用它,长度为5000

Fortigate防火墙设备存身远程代码执行高危漏洞

内存不变,除了字节5000,现在有值0X50和5001,现在为NULL。

这是一个很好的原语:现在有一种方法可以编辑内存中的字节。

更高效率的方法

我们实际上可以做得更好,连续覆盖几个字节,每个字节一个请求。比如想给内存写入e ABC\0,溢出缓冲区后的e ABC\0字节。首先计算一个种子s0

Fortigate防火墙设备存身远程代码执行高危漏洞

然后,计算一个种子s1这样:

Fortigate防火墙设备存身远程代码执行高危漏洞

接着S2种子的:

Fortigate防火墙设备存身远程代码执行高危漏洞

然后将第一个字节设置为零(长度:L):

Fortigate防火墙设备存身远程代码执行高危漏洞

然后以相反的顺序应用带有种子的原语,s2到s0 ,长度L + 1 至L + 3 。得到,对于s2:

Fortigate防火墙设备存身远程代码执行高危漏洞

s1:

Fortigate防火墙设备存身远程代码执行高危漏洞

以及s0:

Fortigate防火墙设备存身远程代码执行高危漏洞

通过使用4原语次,可以成功将ABC写到了内存中。但是,该技术有一个巨大的缺陷,因为它弄乱了修改内存之前的所有内容。

结论

该漏洞Fortinet已于6月8日修复。修复版本为7.2.5、 7.0.12、6.4.13和 6.2.15 ,请低于这些版本的设备抓紧升级。

继续阅读