Apache Httpd AJP请求走私 CVE-2022-26377
[toc]
漏洞描述
Apache HTTP Server的mod_proxy_ajp模块与tomcat ajp解析模块存在差异,当用户配置了mod_proxy_ajp模块与后端tomcat服务器通信时,攻击者能够在正常的HTTP请求中走私一条自定义HTTP请求到后端tomcat。攻击者可利用该漏洞实现对tomcat AJP端口的访问,比如自定义attributes属性值来攻击存在幽灵猫漏洞但AJP协议端口未对外开放的tomcat服务器。
影响范围
受影响版本:
Apache HTTP Server < 2.4.54
漏洞分析
AJP协议分析
ajp数据包格式
1 | AJP13_FORWARD_REQUEST := |
从server发送到container的数据包以 0x1234
两字节魔术头开头,随后两字节代表数据包长度(不包括前4个字节)。
然后是数据部分,除了POST包发送的请求体外,其他包的数据部分的首字节为其消息类型(code)。这里只需要关注 Forward Request
和 Data
。
The web server can send the following messages to the servlet container:
Code | Type of Packet | Meaning |
---|---|---|
2 | Forward Request | Begin the request-processing cycle with the following data |
7 | Shutdown | The web server asks the container to shut itself down. |
8 | Ping | The web server asks the container to take control (secure login phase). |
10 | CPing | The web server asks the container to respond quickly with a CPong. |
none | Data | Size (2 bytes) and corresponding body data. |
The servlet container can send the following types of messages to the web server:
Code | Type of Packet | Meaning |
---|---|---|
3 | Send Body Chunk | Send a chunk of the body from the servlet container to the web server (and presumably, onto the browser). |
4 | Send Headers | Send the response headers from the servlet container to the web server (and presumably, onto the browser). |
5 | End Response | Marks the end of the response (and thus the request-handling cycle). |
6 | Get Body Chunk | Get further data from the request if it hasn’t all been transferred yet. |
9 | CPong Reply | The reply to a CPing request |
apache httpd
Apache 解析AJP协议的部分在 mod_proxy_ajp
模块 。在发送 DATA
类型的数据包时,会在前两位填充数据,分别是魔术头 0x1234
、数据包长度、body
长度。其中数据包长度为 body
长度加2字节,而 body
长度即是 Forward Request
数据包中 Content-Length
的长度。
通过对比 DATA
数据包和 Forward Request
数据包,可以发现由于发送的 DATA
数据包前面填充了六位,因此六位后的数据是我们完全可控的,而前六位中两者只有第五第六位是有差别的。在 Forward Request
数据包中分别对应 prefix_code
和 method
,在 DATA
数据包中对应的是body的长度,因此我们可以通过控制DATA
数据包中 body
的长度令其等于正常Forward Request
数据包的prefix_code
和 method
。
比如正常的GET Forward Request
数据包prefix_code
和 method
分别为 0x02 0x02
,0x0202=514
,即需要填充够514个长度的body。body的具体内容为正常的 Forward Request
数据包6位之后的数据即可。
漏洞利用
我们可以构造一个长度正好为0x202(514)的请求体
可以看到我们发送的data请求第五和第六个字节的确是0202,但这个请求依然被解析成了body,并没有成为Forward Request
,
这是因为tomcat AjpProcessor
在接收到 Content-Length
大于0的请求头时,会调用 AjpProcessor.receive()
读取后续的body数据,我们的第二个请求包依然被当作了一个body而没有被当作一个新的 Forward Request
请求。因此我们需要让其获取到的 Content-Length
不大于0或者不发送这个请求头。
除了 Content-Length
外,还有一种指定HTTP消息实体采用何种编码形式的请求头 Transfer-Encoding
,值得注意的是,在AjpProcessor中并没有对TE的处理,那么我们只要能用TE来通过反代的验证,再将这个请求发给AjpProcessor时,我们的body就会被当成一个新的 Forward Request
请求
我们自然想到可以这样构造
但抓包发现我的chunk数据并没有发送出去
原因在于mod_proxy_ajp.c中对TE请求头有约束,如果取到的TE等于chunked,不会进入后续的else,也就不会发送body
这里可以用a,chunked
的形式来绕过,高版本的apache是允许用逗号来分隔TE的(这里没有细调),我们的request会走到mod_proxy_ajp.c中,同时也能绕过这里的判断
利用这种构造即可成功进行走私,wireshark已经把这个data包识别成了 Forward Request
修复方案
增加了判断,优化了逻辑,只要是取到TE,就不会进入else