本环境是蛇矛实验室基于"火天网演攻防演训靶场"进行搭建,通过火天网演中的环境构建模块,可以灵活的对目标网络进行设计和配置,并且可以快速进行场景搭建和复现验证工作。
Fortinet FortiOS是美国飞塔(Fortinet)公司的一套专用于FortiGate网络安全平台上的安全操作系统。该系统为用户提供防火墙、防病毒、IPSec/SSLVPN、Web内容过滤和反垃圾邮件等多种安全功能。10月10日,Fortinet官方发布安全公告,修复了其多个产品中的一个身份验证绕过漏洞(CVE-2022-40684),其CVSSv3评分为9.8。攻击者可以通过向易受攻击的目标发送特制的 HTTP 或 HTTPS 请求,有权访问管理界面的远程攻击者可以执行管理员操作。
CVE信息中说明了该漏洞为身份认证绕过漏洞,并且可以RCE。在开始分析前,我们需要先搭建漏洞环境。由于该漏洞影响范围如下,这里我使用防火墙的版本为forigate-vm64 7.2.1,内部 Fotios 版本与防火墙版本一致。
FortiOS 版本 7.2.0 - 7.2.1
FortiOS 版本 7.0.0 - 7.0.6
FortiProxy 版本 7.2.0
FortiProxy 版本 7.0.0 - 7.0.6
FortiSwitchManager 版本 7.2.0
FortiSwitchManager 版本 7.0.0
下面开始配置Fortigate防火墙,使其能够与我们的攻击机网络互通。
注意:Fortigate-VM-7.2.0以后使用新的证书方式,需要有fortinet账号获得永久试用,这里需要fortigate联网。
使用浏览器访问ip后,使用admin:password进行登录,登录后界面如下所示。至此,fortigate环境已配置完毕,接下来我们进行分析。
libguestfs 是一组 Linux 下的 C 语言的 API ,用来访问虚拟机的磁盘映像文件,几乎可访问任意类型的文件系统。debian系安装命令为"sudo apt install libguestfs-tools",安装完成后会有很多"virt-开头的命令。
将fortios.vmdk从下载好的fortigate压缩包中解压出来,使用"sudo virt-filesystems -a fortios.vmdk"也是查看磁盘的分区情况。然后使用"sudo guestmount -a fortios.vmdk -m /dev/sda1 --ro mount_dir_name"进行挂载。
mount成功后,rootfs.gz为文件系统压缩包,我们将其复制出来。
rootfs.gz解压后为rootfs,使用"cpio -i 2> /dev/null < rootfs"命令进行提取。提取后发现大量文件系统的目录和.tar.xz文件,.tat.xz文件使用xz解压时会失败,我们可以使用文件系统中自带的(sbin/xz)程序进行解压,这里只需注意它的链接器路径(将其修改至主机系统的链接器路径),我们patch后并保存。
xz解压后提取出各个文件系统目录,进入到bin目录后寻找httpsd程序。httpsd是init程序的软连接,并且这里可以看出init程序非常大,逆向该程序比较费时。所以我们可以根据调试信息先来逆向程序逻辑。
运行以下命令开启httpsd程序的调试信息,当开启调试信息后,当我们对fotigate的web服务进行操作时,该操作的信息就会打印到屏幕上。
diagnose debug enable
diagnose debug application httpsd -1
diagnose debug cli 8
调试开启后,下面以登录授权访问api为例,简单分析一下流程,当我们在浏览器输入admin:password点击登录后访问api打印的调试信息如下
我们根据调试信息打印的字符在程序中进行搜索,根据字符串引用进行定位,发现程序执行了fweb_debug_init函数
大致浏览一遍后发现后发现程序使用了Apache Portable Runtime库,我们可以根据函数库对函数的使用进行查询。这里的apr_table_get函数为从表单中取出key值对应的value。
对fweb_debug_init函数进行交叉引用,发现sub_C4BF20调用了fweb_debug_init函数,这个函数与上面中的调试信息并无联系,我们继续往上跟。
sub_C4C480调用了sub_C4BF20函数,并且后面的fweb_debug_final函数与上面图中最后登录成功后的最后产生的调试信息相同。那么fweb_debug_init与fweb_debug_final之间的俩个函数v3[1]函数和sub_C4C2A0产生了大量调试信息。我们跟进分析一下v3[1]函数
v3由参数a2赋值,a2为sub_C4C480函数的参数,
a2参数为off_3FEA400函数数组的地址
off_3FEA400函数数组为传入sub_C4C480函数的参数,当函数执行完fweb_debug_init后,通过参数加索引的方式调用相应hanler函数,这里v3[1]执行sub_c929F0函数
sub_c929F0函数中调用了api_check_access函数,并且程序会根据api_check_access返回值返回用户对应响应码的reponse。我们进入api_check_access函数中进行查看
api_check_access函数的返回值由几个子函数共同决定,当我们根据调试信息追函数流程时,发现在api_check_access中并没有输出任何调试信息,而是在sub_c929F0函数中调用了handle_cli_request输出了调试信息。同时handle_cli_request函数输出完vdom "root"后,该handler函数执行完毕并返回执行fweb_debug_final函数,随后结束该次event响应。
上面我们大概知道了访问api时程序执行大概流程,但是具体细节和身份验证的流程我们还是不知道。下面我们直接用已公开的poc进行测试,并关注其调试信息以方便逆向。
此时调试如下,我们发现和上面已授权登录相比,多了俩条调试信息,分别是fweb_authorize_all和api_access_check_for_trusted_access,下面我们跟进去分析一下。
当我们跟进去时发现sub_C4AC70函数先调用了sub_C4B590函数,然后ap_hook_handler hook前面我们分析的sub_C4AC60函数。sub_C4B590也同样是ap_hook_check_access_ex hook的fweb_authorize_all函数。那么到此所有的流程我们已经知道了,接下来我们分析一下漏洞是如何形成的。
在认证过程中,函数首先调用fweb_authorize_all判断v2+64是否等于"127.0.0.1",即判断是否本机访问,如果本机访问则sub_C50E80函数内部继续判断接口如果是否为vsys_fgfm接口。随后取Forwarded头的value值,strstr函数查找"for="的位置,随后执行if结构体内容,再次判断Forwarded_header_content_tmp中是否存在"by"字符,如果判断不通过并不会进入到api_check_access函数中。
api_access_check_for_trusted_access函数中调用sub_C510D0,传入参数为"Node.js"
sub_C510D0函数中判断表单中User-Agent的value值是否与Node.js是否相同
如果不是"Node.js",则判断User-Agent的value值是否与Report Runner是否相同
进入到过以上俩种方式中的某一种,用户赋值为"Local_Process_Access",此时会绕过身份认证。也就是说要想攻击成功,需要设置Forwarded头value值必须为"for=",后面可以设置127.0.0.1来隐藏防火墙中的攻击记录,而User-Agent的value可以设置"Node.js"和"Report Runner"俩种中的一个。
以上俩种绕过测试只在Fortigate-VM-7.2.1(Fortigate-VM-7.2.0由于没有镜像所以没有测)有效,当fortigate-vm版本在7.0.0-7.0.5中User-Agent需要为Node.js(7.0.6没测)。
未攻击前,使用ssh连接fortigate的admin用户需要密码登录。
使用kali生成ssh-pulibc-key,然后利用漏洞绕过身份认证,并使用PUT方法设置fortigate的ssh-public-key1(实现这种攻击方式需设置User-Agent头为"Report Runner"),点击send进行攻击(或使用github已公开的exp脚本进行攻击)。
出现"SSH key is good."后,说明攻击成功,此时使用ssh连接fortigate防火墙则需不要输入密码。获取fortigate终端后,可执行任意命令。
我们这一小节简单了解了身份认证绕过流程,分析并复现了fortigate防火墙身份认证绕过漏洞的形成过程以及如何利用。
蛇矛实验室成立于2020年,致力于安全研究、攻防解决方案、靶场对标场景仿真复现及技战法设计与输出等相关方向。团队核心成员均由从事安全行业10余年经验的安全专家组成,团队目前成员涉及红蓝对抗、渗透测试、逆向破解、病毒分析、工控安全以及免杀等相关领域。