CVE-2024-50379-Tomcat-rce

First Post:

Last Update:

CVE-2024-50379-Tomcat-rce

🤔 此漏洞有个前生 CVE-2017-12615

在 Apache Tomcat 7.0.0 - 7.0.79 范围中,如果 web.xml 文件中,readonly 被设置成 false,我们就能用 put 方法(前提是没被禁用)上传一个精心打造的 jsp 文件直接来达到上传 webshell 的目的,简单粗暴

不是本次重点,不多述

接下来是复现部分

环境:

首先我们需要 windows 环境,只有对大小不敏感的系统才能成功 rce

受影响的 Apache Tomcat 版本包括:

  • 11.0.0-M1 <= Apache Tomcat < 11.0.2
  • 10.1.0-M1 <= Apache Tomcat < 10.1.34
  • 9.0.0.M1 <= Apache Tomcat < 9.0.98()

复现:

下载有洞的版本后,我们需要先设置其中的 web.xml,在其中添加一个参数



将 readonly 指定为 false,这样才能让 defultServlet 允许上传文件到服务器

然后直接点击 startup.bat 启动,这里可能会编码错误,因为 windows 一般使用 GBK,我们需要在 logging.properties 文件中修改一下编码方式



直接双击 startup.bat 启动



卡在这也没事,直接访问本地的 8080 端口,即可

当我们尝试将一个 a.jsp 文件上传的时候,会发现 404 错误、

Tomcat 是禁止直接上传 .jsp 文件,后缀为小写的 .jsp 文件,当用户访问时,Tomcat 会交给 jspServlet 处理,而他并不处理 put 方法,导致上传失败。



所以我们需要先让文件能被 put 上去,我们选择将文件改为 a.Jsp 这样处理他的就是 defaultsevrlet,文件可以顺利上传



回应 201(即上传成功),本地也能看见



(这时候应该可以写个 webshell,用蚁剑直接连上(没试过

但显然我们可以做的更多,接下来进行 rce

这时候,如果我们的文件中有精心构造的内容,我们如何让他执行呢

(本来我是使用 yakit 开启多个线程,一个不断 put .Jsp 文件,另一个不断 GET .jsp 文件来实现条件竞争达成 rce,但是一直不成功,这里使用佬佬的 poc 完成 rce

工具作者的 github 地址:https://github.com/SleepingBag945/CVE-2024-50379



当我们访问地址时,成功弹出了计算器



工具源码分析:

自己看

主要在 main 函数中,通过开启三个线程,两个不断 put,一个 get,重复到成功

原理解释:

对于 win 来说,a.Jsp 和 a.jsp 是一个文件(这个也可以研究 windows 找源码的方法知道为什么,这里不多解释。

我们要在 GET a.jsp 时让 JspServlet 执行这个文件,这里涉及 AbstractFileResourceSet 的 file 方法

try {
    // JRE 方法
    canPath = file.getCanonicalPath();
    } catch (IOException var6) {
    }

if (canPath != null && canPath.startsWith(this.canonicalBase)) {
    String absPath = this.normalize(file.getAbsolutePath());
    ....
    if (!canPath.equals(absPath)) {
        if (!canPath.equalsIgnoreCase(absPath)) {
            this.logIgnoredSymlink(this.getRoot().getContext().getName(), absPath, canPath);}
            return null;
        } else {
            return file;
         }
 //大概就这些

我们发现只有当 abspath=canpath 时才会返回文件,也就是才会执行代码

其中 abs path 是用户输入的路径拼接处理后的本地绝对路径

其中 can path 是 JRE 类 WinNTFileSystem JNI/cache 处理后得到的路径(如果缓存启用就会从其中获取

当我们 PUT 一个 a.Jsp 文件上去时,显然 abspath 和 canpath 都是 a.Jsp

当我们 GET a.jsp 时,如果我们已经 PUT a.Jsp,那么,abspath 会是 a.jsp canpath 会是 a.Jsp(从缓存中读),那么不通过检测,就不会返回文件而如果我们 PUT 的 a.Jsp 还没落地 那么 abspath canpath 都会是 a.jsp 这样会返回文件

但很显然不 PUT 就不会有文件,而我们查看代码可以知道,这种检查不会只有一次而是重复好几次

org.apache.catalina.webresources#getResource
cacheEntry.validateResource(useClassLoaderResources);
//在 JspServlet 后续处理的过程中,会再次抵达 AbstractFileResourceSet file 方法
//类似的过程会重复好几次

在 JspServlet 读取文件前会经过很多次的检查

所以,我们要保证在它检查的时候,我们的 abspath canpath 要一直保持 a.jsp 而在返回文件前一刻,我们的 PUT a.Jsp 文件要刚好落地,这样才能完美执行,这里就是一个条件竞争