CVE-2023-46747漏洞复现
免责声明
漏洞复现过程全部在本地虚拟环境下进行,请勿用于恶意攻击,未授权的攻击属于非法行为!传播、利用本文章所提供的信息而造成的任何直接或者间接的后果及损失,均由使用者本人负责,作者不为此承担任何责任。
漏洞描述
F5 BIG-IP是由美国F5 Networks公司开发的一种应用交付控制(Application Delivery Controller,ADC)解决方案。它提供负载均衡、SSL加速、应用安全、Web应用防火墙、WAN优化和其他功能。是一个VPN防火墙设备。
NVD与2023年10月26日发布CVE-2023-46747漏洞。该漏洞可以让未经验证的攻击者,通过管理端口来访问BIG-IP设备,执行任意系统命令。该漏洞评分为9.8。可以使用更新版本、运行F5官网发布的补丁来进行紧急设备。
影响版本
| BIG-IP系列 | 影响版本 | 修复版本 |
|---|---|---|
| 13.x | 13.1.0 - 13.1.5 | 13.1.5.1 |
| 14.x | 14.1.0 - 14.1.5 | 14.1.5.6 |
| 15.x | 15.1.0 - 15.1.10 | 15.1.10.2 |
| 16.x | 16.1.0 - 16.1.4 | 16.1.4.1 |
| 17.x | 17.1.0 | 17.1.0.3 |
漏洞原理
到官网去申请试用,选择一个有漏洞的虚拟版本下载运行。查看官网补丁,从官网的补丁mitigation.txt中可以看出,F5修改了一些web服务配置。分别修改了httpd(apache)的proxy_ajp.conf文件和tomcat的server.xml文件。启用了ajp协议的secret功能。
proxy_ajp.conf:
1 | LoadModule proxy_ajp_module modules/mod_proxy_ajp.so |
server.xml
1 | <?xml version='1.0' encoding='utf-8'?> |
可以看出,这又是httpd和tomcat对数据处理不一致导致的问题。早在2020年,F5 BIG-IP就爆出过CVE-2020-5902,当时httpd与tomcat在处理URL时候存在差异导致了RCE,但这次问题出在ajp1.3协议上。ajp1.3是定向包协议。因为性能原因,使用二进制格式来传输可读性文本。WEB服务器通过TCP连接和SERVLET容器连接。为了减少进程生成socket的花费,WEB服务器和SERVLET容器之间尝试保持持久性的TCP连接,对多个请求/回复循环重用一个连接。这个协议在2020年爆了请求走私漏洞CVE-2022-26377。
这个请求走私漏洞影响版本为Apache HTTP Server < 2.4.54。F5 BIG-IP的httpd版本是2.4.6,但它是定制版,目前官网上还没有这个版本。F5 BIG-IP使用的httpd仍然受这个漏洞的影响。通过学习ajp漏洞原理和ajp协议包的构造方式,我们恶意构造、执行任意ajp协议请求。
漏洞复现
复现实验在BIG-IP 17.1.0上进行。F5 BIG-IP的httpd受AJP请求走私的影响,攻击者可以构造任意AJP请求执行。在proxy_ajp.conf中可以看到/tmui/Control/form是走AJP协议的:
1 | ProxyPassMatch "^/tmui/Control/form(\??)$" "ajp://localhost:8009/tmui/Control/form$1" retry=5 |
在这个路由下,可以通过/tmui/system/user/create.jsp创建用户。也就是说,通过构造AJP请求走私,可以在未授权的情况下通过该路由创建用户。
创建用户后可以通过/mgmt/shared/authn/login功能可以给登录用户提供一个token,使用该token作为X-F5-Auth-Token可以通过/mgmt/tm/util/bash来执行任意系统命令(CVE-2022-1388)。
漏洞利用步骤如下:
- 构造AJP请求走私,通过/tmui/Control/form访问/tmui/system/user/create.jsp,创建任意用户。
- 通过PATCH /mgmt/shared/authz/users/{用户名}来更新密码有效期(创建用户后通过配置界面登录时会提示密码过期,需要重置密码。)
- 使用该用户通过/mgmt/shared/authn/login登录,获取token。
- 通过token访问/mgmt/tm/util/bash,执行任意指令。
以上的请求都可以通过本地模拟执行后抓包来进行查看修改。在F5 BIG-IP上使用tcpdump可以将mgmt接口的流量抓获保存为pcapng格式,导出到wireshark进行查看。
构造AJP请求走私
AJP请求走私的原理请大家看参考文献进行学习,它的原理与http请求走私类似。http请求的编码格式为Transfer-Encoding而不是Content-Length时,tomcat不会调用AjpProcessor.receive()来接受body数据,同时也不会做任何处理。所以后续的数据会被当作Forward request。

而mod_proxy_ajp.c中虽然对Transfer-Encoding: chunked进行了过滤,但攻击者可以通过Transfer-Encoding: a, chunked来进行绕过。

所以,http请求的传输编码构造为Transfer-Encoding: a, chunked,即可将分块传输的数据作为Forward Request进行提交。
根据AJP1.3协议文档(其实官方文档并不是协议开发者写的,这个协议实在是有些老。),可以知道,Forward Request是server转发到container的请求,前两个字节为0x1234,紧跟的两个字节为数据包长度(不包括前四个字节),第五个字节是2,第六个字节是方法(如果是POST方法则为4)。之后的数据都是请求体。
而对于Data包,前四个字节和Forward Request是一样的,但第五个字节、第六个字节是Data包的长度(实际上,比第三个字节和第四个字节表示的数据包长度的值小2)。**构造Data包时,将Data包长度控制为0x0204,则可在走私时让第五个字节和第六个字节分别为0x02(对应Forward Request)、0x04(POST方法)。**这样Data包就会当作Forward Request进行处理,从而提交POST包。


既然我们有本地环境,直接正常地创建一个用户,然后用tcpdump抓包导入wireshark查看具体内容,走私时可以直接重复利用该内容。
目前,github上已经有了poc,走私时直接重复利用AJP协议包内容也是可以的。如果自己动手构造AJP协议包,需要阅读AJP文档,尤其需要注意AJP协议的字符串定义和我们平常使用的字符串定义不同。

我们直接将这个数据包拉下来,然后使用AJP请求走私的方式重新投放即可,注意原数据包大小比较大,需要删除很多数据,但需要保留tmsh的防csrf检测部分。走私的请求体长度应该正好为0x204。

其中tmsh的防csrf检测部分原理为:

更新有效期
这一步可以手动通过配置页面登录来进行更新,也可以通过Burpsuite发包实现,也可以通过python脚本集成该功能。我编写的python exp在参考文献中给出,下面不详细给出exp的具体内容。

这一步需要填写Authorization头,内容为Basic+空格+base64(用户名+冒号+密码)。数据部分需要填充oldPassword和password,不变即可。
获取token
使用Burpsuite工具发包。
命令执行
获取token后,轻松执行任意系统命令:
通过脚本也可以直接一步到位:

参考文献
[1] https://h4cking2thegate.github.io/posts/38810/index.html
[2] https://www.ctfiot.com/44809.html
[4] https://github.com/wbohan/CVE/blob/main/CVE-2023-46747.py
