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

跟进 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 复现过程

tomcatPersistentManager 支持通过使用 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 ,我这里的 sessionid1305E82A6466DA5ABB5E8EABB1A85369 ,所以创建文件为 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 继承的是 tomcatStandardSession

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

Reference

Tomcat - 持久化 Session

8 个赞

卧槽!!师傅牛逼啊!!!!羡慕了