Tomcat Remote Code Execution via session persistence 分析【CVE-2020-9484】

0x01 概述

Tomcat 在2020年5月11日时候修复了一个 Remote Code Execution via session persistence 漏洞,这个洞利用条件有点苛刻。

0x02 漏洞分析

漏洞点在 org/apache/catalina/session/FileStore#load ,这里 tomcat 本地会有个文件,这个文件会记录 SESSION 信息,当加载这个 SESSION 信息文件的时候漏洞就出来了,具体看下图,他会先创建一个空Session用来接收这个 Session 文件里面的 Session

image-20200521100731115

跟进 readObjectData ,来到 org/apache/catalina/session/StandardSession 这里。

1
2
3
public void readObjectData(ObjectInputStream stream) throws ClassNotFoundException, IOException {
this.doReadObject(stream);
}

继续跟进 doReadObject 这几个 readObject 眼熟吧。

1
2
3
4
5
6
7
8
9
10
protected void doReadObject(ObjectInputStream stream) throws ClassNotFoundException, IOException {
this.authType = null;
this.creationTime = (Long)stream.readObject();
this.lastAccessedTime = (Long)stream.readObject();
this.maxInactiveInterval = (Integer)stream.readObject();
this.isNew = (Boolean)stream.readObject();
this.isValid = (Boolean)stream.readObject();
this.thisAccessedTime = (Long)stream.readObject();
this.principal = null;
this.id = (String)stream.readObject();

问题来了,这个洞如何利用呢,很简单你写一个文件上传,跨目录的,生成一个 Session 文件覆盖这个文件就行,所以你需要两个前置条件,一个文件上传,一个知道绝对路径,当然相对应该也可以。

0x03 复现过程

tomcatPersistentManager 支持通过使用 Store 将内存中的 session 拷贝至文件或数据库中。如果当前活动的 session 对象数量超过了上限值或者 session 对象闲置了过长时间,就会有 session 对象就会被换出,存储到磁盘中,以节省内存空间。当 Tomcat 正常关闭、重启或 Web 应用程序重新加载时,PersistentManager 也会像 StandardManager 一样,将 session 对象持久化到磁盘中。

conf/context.xml文 件中配置下列内容,开启 session 持久化。

1
2
3
4
5
6
7
8
9
<Manager className="org.apache.catalina.session.PersistentManager" 
debug="0"
saveOnRestart="false"
maxActiveSession="-1"
minIdleSwap="-1"
maxIdleSwap="-1"
maxIdleBackup="-1">
<Store className="org.apache.catalina.session.FileStore" directory="./session" />
</Manager>

然后用yso创建一个序列化文件,命名为 sessionID.session ,我这里的 sessionid1305E82A6466DA5ABB5E8EABB1A85369 ,所以创建文件为 1305E82A6466DA5ABB5E8EABB1A85369.session

image-20200521105043651

image-20200521105208257

然后按照上述所说之需要重启、重新加载就会生效。

image-20200521105322936

所以能理解第二部分说的这里需要一个跨目录上传的利用点,以及觉得绝对路径了吧。

截止到这里是我给官方提交的版本,后续根据作者提交的内容看了眼代码,实际上这里的 file 是可控的,是前面说过的 sessionID ,所以这里 sessionID 实际上可以命名为 ../../../../xxx 这样只需要上传的相对路径,且后缀名 .session 可控的情况下就可以加载了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public Session load(String id) throws ClassNotFoundException, IOException {
// Open an input stream to the specified pathname, if any
File file = file(id);
if (file == null || !file.exists()) {
return null;
}
...
}

private File file(String id) throws IOException {
if (this.directory == null) {
return null;
}
String filename = id + FILE_EXT;
File file = new File(directory(), filename);
return file;
}

0x04 修复

修复也很简单在FileStore.java动手了

image-20200521105833390

0x05后话

这个洞我也给过去tomcat,当然他们没认我的,他们不认为这个是漏洞,所以我在公开一个场景。

image-20200521105409375

一般现在持久化做的比较多的还是在redis上,比较常见的是类似这个项目

RedisSession 继承的是 tomcatStandardSession

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import org.apache.catalina.Manager;
import org.apache.catalina.session.StandardSession;
import java.util.HashMap;
import java.io.IOException;

import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory;


public class RedisSession extends StandardSession {
...
public void readObjectData(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException {
super.readObjectData(in);
this.setCreationTime(in.readLong());
}
}

来到跟进super.readObjectData,Vulnerability exists in org.apache.catalina.session.StandardSession

image-20200224223044099

漏洞利用

redis 里面的 keyJSESSIONIDvalue ,而 value 则是序列化数据。配合 redis 未授权漏洞修改这部分序列化数据,配合 gadget,就会触发 session 反序列化漏洞,当然这部分他们也没处理,既然认为不是漏洞我就公开了。

image-20200225104438902

image-20200224224411342

Reference

Tomcat - 持久化 Session