当 Tomcat 运行在 Windows 主机上,且启用了 HTTP PUT 请求方法,攻击者通过构造的攻击请求向服务器上传包含任意代码的 JSP 文件,可造成任意代码执行
受影响版本:Tomcat 7.0.0 – 7.0.79(7.0.81修复不完全)
漏洞前提:开启PUT上传(修改web.xml配置文件开启PUT上传)、web.xml中readOnly属性为false(Apache Tomcat 7 默认值是 true)
这个漏洞的根本是通过构造特殊后缀名,绕过了 tomcat 检测,让它用 DefaultServlet 的逻辑去处理请求,从而上传 jsp 文件
目前主要三种方法:
漏洞原理
Tomcat在处理请求时有两个默认的Servlet,一个是DefaultServelt,另一个是JspServlet。两个Servlet被配置在 Tomcat的web.xml中
JspServlet只处理后缀为.jsp 和.jspx的请求。其他请求都由DefaultServlet进行处理,从这一点可以理解为何 PUT请求时 URI为“/1.jsp/”而不直接使用“/1.jsp”,因为直接PUT 请求“/1.jsp”会由JspServlet进行处理,而不是由DefaultServlet处理,无法触发漏洞
想要实现一个Servlet,就必须要继承HttpServlet,DefaultServlet也不例外。在HttpServlet中有一个doPut方法用来处理PUT方法请求,DefaultServlet重写了该方法
protected void doPut(HttpServletRequest req, HttpServletResponse resp) throws ServletException,IOException {
if (readOnly) {
resp.sendError(HttpServletResponse.SC_FORBIDDEN);
return;
}
String path = getRelativePath(req);
InputStream resourseInputStream = null;
......
if(resources.write(path,resourceInputStream,true))
}
该方法的开端就判断了一个readOnly属性,当结果为true时会直接返回403,所以要将该值设置为false。readOnly属性的值来源于Tomcat 的web.xml的配置,在DefaultServlet的配置中添加一项参数,如下所示。Tomcat启动时会读取web.xml,并在用户第一次请求时将DefaultServlet的readOnly属性赋值为false
<init-param>
<param-name>readonlyparam-name>
<param-value>falseparam-value>
init-param>
doPut方法的关键点在于resources.write (path, resourceInputStream, true) path变量存放的PUT请求的URI
doPut方法的代码如下图所示,在第184行,path作为参数传入了main.write方法中,并继续执行
当执行到dest = file(path.substring(webAppMount.length())时, false); path被作为参数再次传入,所以选择执行file方法,截取部分代码如下所示
protected final File file(String name,boolean mustExist) {
if(name.equals("/")){
name = "";
}
File file = new File(fileBase, name);
.......
}
file方法中实例化了一个File对象用户后续向目录中写入请求正文中的内容,name参数是我们PUT请求的URI
fileBase参数就是当前Web应用所在的绝对路径
在File对象实例化的过程中会处理掉URL“/1.jsp/”的最后一个“/”以及多余的“/”符号,例如“/com///Test//FileTest//1.jsp/”经过处理会变成“/com/Test/FileTest/1.jsp”,因此,通过PUT请求,“/1.jsp/”可以达到上传任意文件的目的
https://paper.seebug.org/399/