0x01 概述
Tomcat 在2020年5月11日时候修复了一个 Remote Code Execution via session persistence 漏洞,这个洞利用条件有点苛刻。
0x02 漏洞分析
漏洞点在 org/apache/catalina/session/FileStore#load ,这里 tomcat 本地会有个文件,这个文件会记录 SESSION 信息,当加载这个 SESSION 信息文件的时候漏洞就出来了,具体看下图,他会先创建一个空Session用来接收这个 Session 文件里面的 Session 。
跟进 readObjectData ,来到 org/apache/catalina/session/StandardSession 这里。
public void readObjectData(ObjectInputStream stream) throws ClassNotFoundException, IOException {
this.doReadObject(stream);
}
继续跟进 doReadObject 这几个 readObject 眼熟吧。
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 复现过程
tomcat 在 PersistentManager 支持通过使用 Store 将内存中的 session 拷贝至文件或数据库中。如果当前活动的 session 对象数量超过了上限值或者 session 对象闲置了过长时间,就会有 session 对象就会被换出,存储到磁盘中,以节省内存空间。当 Tomcat 正常关闭、重启或 Web 应用程序重新加载时,PersistentManager 也会像 StandardManager 一样,将 session 对象持久化到磁盘中。
在 conf/context.xml文 件中配置下列内容,开启 session 持久化。
<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 ,我这里的 sessionid 为 1305E82A6466DA5ABB5E8EABB1A85369 ,所以创建文件为 1305E82A6466DA5ABB5E8EABB1A85369.session
然后按照上述所说之需要重启、重新加载就会生效。
所以能理解第二部分说的这里需要一个跨目录上传的利用点,以及觉得绝对路径了吧。
截止到这里是我给官方提交的版本,后续根据作者提交的内容看了眼代码,实际上这里的 file 是可控的,是前面说过的 sessionID ,所以这里 sessionID 实际上可以命名为 ../../../../xxx 这样只需要上传的相对路径,且后缀名 .session 可控的情况下就可以加载了。
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动手了
0x05后话
这个洞我也给过去tomcat,当然他们没认我的,他们不认为这个是漏洞,所以我在公开一个场景。
一般现在持久化做的比较多的还是在redis上,比较常见的是类似这个项目
RedisSession 继承的是 tomcat 的StandardSession。
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
漏洞利用
redis 里面的 key 是 JSESSIONID 的 value ,而 value 则是序列化数据。配合 redis 未授权漏洞修改这部分序列化数据,配合 gadget,就会触发 session 反序列化漏洞,当然这部分他们也没处理,既然认为不是漏洞我就公开了。