Apache 在2017年9月19日发布并修复了CVE-2017-12616和 CVE-2017-12615两个高危漏洞,并且在Apache Tomcat 7.0.81进行了修复。
Tomcat 安全漏洞发布地址:
https://tomcat.apache.org/security-7.html
CVE-2017-12616(信息泄露):允许未经身份验证的远程攻击者查看敏感信息。如果tomcat开启VirtualDirContext有可能绕过安全限制访问服务器上的JSP文件源码。Apache security Team在2017年8月10号已经识别该漏洞,2017年9月19日已经发布修复该漏洞最新版本的tomcat 7.0.81。影响范围为:7.0.0 to 7.0.80。
CVE-2017-12615(远程代码执行漏洞):360观星实验室(360-sg-lab)在2017年7月26向apache security Team报告了该漏洞。Tomcat7服务器允许进行HTTP PUTs操作(例如通过设置初始化参数readonly默认值为false),攻击者通过构造的恶意请求可以上传JSP的webshell,webshell可以在服务器上执行任意的操作。影响范围为:7.0.0-7.0.79。
Apache security team本次发布的两个漏洞涉及到tomcat两个重要的处理Http请求的Servlet,分别为org.apache.catalina.servlets.DefaultServlet和org.apache.jasper.servlet.JspServlet。其中DefaultServlet主要用于处理静态资源如html、image等。JspServlet用于处理动态页面请求如JSP、JSPX等。
图1 conf/web.xml配置
什么时候调用哪个Servelt?这个决定取决于tomcat请求路由核心组件org.apache.tomcat.util.http.mapper.Mapper
,Mapper定义了多个规则判断客户端的请求该使用哪个Servlet。
漏洞触发的先决条件是需要在conf/server.xml配置VirtualDirContex
参数,默认情况下tomcat7并不会对该参数进行配置。VirtualDirContex
主要使用场景是在IDE开发环境,生产环境tomcat官方不建议开启。正常情况下一般不会配置该参数开启,因此该漏洞显得有点鸡肋。
图2 VirtualDirContext类关系图
VirtualDirContext
是FileDirContext
的子类,它允许在单独的一个webapp应用下对外暴露出多个文件系统的目录。实际项目开发过程中,为了避免拷贝静态资源(如images等)至webapp目录下,tomcat推荐的做法是在server.xml配置文件中建立虚拟子目录。
图3 项目结构
例如:我们项目的webapp目录为F:\site\cve-2017-12616,静态资源的目录为F:\site\images。现在我们想将两个目录合并为一个名义上的一个目录,需要在tomcat的conf/server.xml的<Host></host>
节点下增加虚拟目录配置。
<Context path="/site" docBase="F:\site\cve-2017-12616\src\main\webapp" reloadable="true" debug="1">
<!-- 配置项目的资源 -->
<Resources className="org.apache.naming.resources.VirtualDirContext" extraResourcePaths="/WEB-INF/classes=F:/site/cve-2017-12616/target/classes,/images=F:/site/images" />
<!-- 配置项目的 classpath -->
<Loader className="org.apache.catalina.loader.VirtualWebappLoader" virtualClasspath="/WEB-INF/classes=F:/site/cve-2017-12616/target/classes" />
<!-- 扫描 jar 的内容 -->
<JarScanner scanAllDirectories="true" />
</Context>
这个时候我们tomcat7服务器重启,打开浏览器访问以下地址,实际上就是完整在浏览器上展示出一张图片。
图4 通过虚拟目录访问静态资源
让我们重新回到漏洞本身,tomcat开启VirtualDirContext
有可能绕过安全限制访问服务器上的JSP源码。如果我们临时对外开放了一个目录/temp=F:/site/cve-2017-12616/src/main/webapp,且该目录包含了很多敏感的JSP代码,那么敏感信息在不用授权的情况下将被泄露出去。因此生产环境不建议开启。另外tomcat本质上是Servlet容器引擎,tomcat访问所有资源都用Servlet来实现的,它的优势不在于处理静态资源,性能与响应时间与专门处理静态资源的服务器相比有差距。静态资源一般由前端代理服务器如nginx、apache来进行处理。
<Resources className="org.apache.naming.resources.VirtualDirContext"
extraResourcePaths="/WEB-INF/classes=F:/site/cve-2017-12616/target/classes,/images=F:/site/images,/temp=F:/site/cve-2017-12616/src/main/webapp" />
JspServlet负责处理所有JSP和JPSX类型的动态请求,DefautServelt负责处理静态资源请求。因此,就算我们构造请求直接上传JSP webshell显然是不会成功的。
图5 构造请求
该漏洞实际上是利用了windows下文件名解析的漏洞来触发的。精心构造的恶意请求会绕过JspServelt,从而由DefaultServlet
来处理该请求。
图6 DefaultServlet
图7 DefaultServlet
serverResource
方法首先会根据请求的path查询缓存中是否存请求资源缓存CacheEntry
。如果存在则直接返回,不存在则构建缓存条目。如果cacheEntry.context
为null
而且path.endsWith("/") || (path.endsWith("\\"))
,则直接向客户端返回404,所以采用Malicious.jsp/方式并不能够成功触发获取服务端漏洞的JSP代码。
虚拟目录文件内容由VirtualDirContext
处理,非虚拟目录文件内容由FileDirContext
处理。FileDirContext
存在一个名为file的检查文件路径的方法。file方法并不能阻止恶意构造的请求文件路径如:
protected File file(String name) {
File file = new File(base, name);
if (file.exists() && file.canRead()) {
if (allowLinking)
return file;
// Check that this file belongs to our root path
String canPath = null;
try {
canPath = file.getCanonicalPath();
} catch (IOException e) {
// Ignore
}
if (canPath == null)
return null;
// Check to see if going outside of the web application root
if (!canPath.startsWith(absoluteBase)) {
return null;
}
// Case sensitivity check - this is now always done
String fileAbsPath = file.getAbsolutePath();
if (fileAbsPath.endsWith("."))
fileAbsPath = fileAbsPath + "/";
String absPath = normalize(fileAbsPath);
canPath = normalize(canPath);
if ((absoluteBase.length() < absPath.length())
&& (absoluteBase.length() < canPath.length())) {
absPath = absPath.substring(absoluteBase.length() + 1);
if (absPath == null)
return null;
if (absPath.equals(""))
absPath = "/";
canPath = canPath.substring(absoluteBase.length() + 1);
if (canPath.equals(""))
canPath = "/";
if (!canPath.equals(absPath))
return null;
}
} else {
return null;
}
return file;
}
图7 VirtualDirContext提供的file方法
如果将文件名修改成”click.jsp%20” 或者 “click.jsp::$DATA”成功获取JSP文件源码。
图8 构造恶意请求获取JSP
图9 构造恶意请求获取JSP
上述分析还遗留了一个问题,为什么Java 语言中的File对象能够正确处理下面三种路径的文件路径。
第一种方式”Malicious.jsp/”,在文件后缀名末尾处增加特殊字符”/”
调试源码发现,java.io.Win32FileSystem的normalize将文件后缀末尾进行规范化处理去掉了尾部的”\”。
第二种方式”Malicious.jsp ”,在文件名后缀末尾增加一个或多个空格。
Open是一个Java native方法,Java无法直接访问到操作系统底层(如系统硬件等),为此Java使用native方法来扩展Java程序的功能。open具体实现细节只能参考开源的JDK 的C源码了。
第三仲方式“Malicious.jsp::$DATA”,在文件名后缀末尾处增加“::$DATA”。
By default, the default data stream is unnamed. To fully specify the default data stream, use "filename::$DATA",
where $DATA is the stream type. This is the equivalent of "filename". You can
create a named stream in the file using the file naming conventions**. Note that "$DATA" is a legal stream name.
从执行结果可以看出,windows会将文件名后缀的所有空格去掉,在windows环境下载文件名的后缀增加特殊字符::$DATA对原文件名相等。
conf/web.xml默认配置 readOnly值为true,禁止HTTP进行 PUT和DELTE类型请求。
为了复现漏洞,将readOnly设置为false。
JspServlet负责处理所有JSP和JPSX类型的动态请求,从代码没有发现处理HTTP PUT类型的操作, PUT 以及 DELTE 等HTTP操作由DefautServelt实现。因此,就算我们构造请求直接上传JSP webshell显然是不会成功的。该漏洞实际上是利用了windows下文件名解析的漏洞来触发的。
说明:Http定义了与 服务器的交互方法,其中除了一般我们用的最多的GET,POST 其实还有PUT和DELETE。根据RFC2616标准(现行的HTTP/1.1)其实还有OPTIONS, HEAD, PUT,DELETE,TRACE,CONNECT等方法。
图4的请求测试结果表明了上诉的猜测结论是正确的。JspServlet负责处理所有JSP和JPSX类型的动态请求,不能够处理PUT方法类型的请求。
利用文件解析漏洞采用PUT方式上传jsp webshell文件。其中文件名设为/malicious.jsp%20。如果文件名后缀是空格那么将会被tomcat给过滤掉。
webshell执行结果
利用Java File的特性,将上传文件名设置为/malicious1.jsp/。Webshell也可以正常上传至tomcat服务器。Java的File对象会将末尾的”/”去掉,因此可以成功上传webshell。很显然这种绕过利用方式存在于所有版本的tomcat以及linux、windows等操作系统。
基于File特性上传webshell
webshell被成功上传
将文件名后缀修改为::$DATA,如malicous.jsp::$DATA会被JDK认为是malicous.jsp,也能够成功上传webshell。
搭建及运行漏洞环境:
docker build -t cve201712615/tomcat:7.0.73 . --rm=true
docker images cve201712615/tomcat
docker run -it -p 1116:8080 镜像前4位
docker靶机webshell执行结果
docker配置Git地址:https://github.com/JavaPentesters/java_vul_target