幽灵猫 Tomcat文件读取/任意文件包含漏洞(CVE-2020-1938 / CNVD-2020-10487)

2024-01-31 18:10

本文主要是介绍幽灵猫 Tomcat文件读取/任意文件包含漏洞(CVE-2020-1938 / CNVD-2020-10487),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

目录

一、漏洞简介

二、漏洞原理:

1.文件读取漏洞

2.任意代码执行漏洞

三、修复建议


学习该漏洞时我翻阅了大量的博客,但是感觉大部分没有说到点子上,所以写这篇博客,记录一下学习历程,接下来我会从代码和协议两个方面来分析这个漏洞。

一、漏洞简介

该漏洞是由长亭科技安全研究员发现的存在于 Tomcat 中的安全漏洞,由于 Tomcat AJP 协议设计上存在缺陷,攻击者通过 Tomcat AJP Connector 可以读取或包含 Tomcat 上所有 webapp 目录下的任意文件,例如可以读取 webapp 配置文件或源代码。此外在目标应用有文件上传功能的情况下,配合文件包含的利用还可以达到远程代码执行的危害。

影响版本:

                Apache Tomcat 6

                Apache Tomcat 7 < 7.0.100

                Apache Tomcat 8 < 8.5.51

                Apache Tomcat 9 < 9.0.31

二、漏洞原理:

首先还是了解这个漏洞要有一些基础知识

这个漏洞是tomcat的漏洞,所以我们要对tomcat的运行原理有一些概念。

tomcat的大致体系结构如下:

 Connector :用于在指定的端口上侦听客户请求,接收连接请求之后分配线程让 Container 来处理这个请求。 

Container :由四个自容器组件构成,分别是Engine、Host、Context、 Wrapper。

Engine :Engine 容器,定义了一些基本的关联关系。

Host :Host 是 Engine 的字容器,Host 在 Engine 中代表一个虚拟主机,其作用 就是运行多个应用。

Context :Context 容器,拥有 Servlet 运行的基本环境,且负责管理其中的 Servlet 实例。 Wrapper :Wrapper 是最底层的容器,负责管理一个 Servlet。

Servlet :服务程序。

(1) Tomcat Connector(连接器)

首先来说一下Tomcat的Connector组件,Connector组件的主要职责就是负责接收客户端连接客户端请求的处理加工。每个Connector会监听一个指定端口,分别负责对请求报文的解析和响应报文组装,解析过程封装Request对象,而组装过程封装Response对象。

上面说了那么多,其实意思就是Connector是tomcat的入口,一个其他博主的例子,如果把Tomcat比作一个城堡,那么Connector组件就是城堡的城门,为进出城堡的人们提供通道。当然,可能有多个城门,每个城门代表不同的通道。而Tomcat默认配置启动,开了两个城门(通道):一个是监听8080端口的HTTP Connector,另一个是监听8009端口的AJP Connector

Tomcat组件相关的配置文件是在conf/server.xml,配置文件中每一个元素都对应了Tomcat的一个组件(可以在配置文件中找到如下两项,配置了两个Connector组件):

    <Connector port="8080" protocol="HTTP/1.1"connectionTimeout="20000"redirectPort="8443" />
 <Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />

HTTP Connector很好理解,通过浏览器访问Tomcat服务器的Web应用时,使用的就是这个连接器;

AJP Connector是通过AJP协议和一个Web容器进行交互。在将Tomcat与其他HTTP服务器(一般是Apache )集成时,就需要用到这个连接器。AJP协议是采用二进制形式代替文本形式传输,相比HTTP这种纯文本的协议来说,效率和性能更高,也做了很多优化。

两个Connector的原理如下图所示: 

我们的浏览器肯定是不支持AJP协议的,所以要在AJP Connector前面加一个apache server的反向代理,我们先将http请求交到反向代理服务器,反向代理服务器再通过ajp协议在8009端口向tomcat发出请求。

(2)Servlet(服务程序)

Servlet意为服务程序,也可简单理解为是一种用来处理网络请求的一套规范。主要作用是给上级容器(Tomcat)提供doGet()和doPost()等方法,其生命周期实例化、初始化、调用、销毁受控于Tomcat容器。有个例子可以很好理解:想象一下,在一栋大楼里有非常多特殊服务者Servlet,这栋大楼有一套智能系统帮助接待顾客引导他们去所需的服务提供者(Servlet)那接受服务。这里顾客就是一个个请求,特殊服务者就是Servlet,而这套智能系统就是Tomcat容器。

tomcat默认配置了两个Servlet,一个是Jsp Servlert,另一个是Default Servlet,第一个是专门负责处理jsp类型的文件的,除了jsp文件的全都交给Default Servlet处理。Tomcat中Servlet的配置是在conf/web.xml 下面。

<!-- The default servlet for all web applications, that serves static    --><!-- resources.  It processes all requests that are not mapped to other   --><!-- servlets with servlet mappings. --><servlet><servlet-name>default</servlet-name><servlet-class>org.apache.catalina.servlets.DefaultServlet</servlet-class>............</servlet><!-- The JSP page compiler and execution servlet, which is the mechanism  --><!-- used by Tomcat to support JSP pages.  Traditionally, this servlet    --><!-- is mapped to the URL pattern "*.jsp". --><servlet><servlet-name>jsp</servlet-name><servlet-class>org.apache.jasper.servlet.JspServlet</servlet-class>............</servlet>............<!-- The mapping for the default servlet --><servlet-mapping><servlet-name>default</servlet-name><url-pattern>/</url-pattern></servlet-mapping><!-- The mappings for the JSP servlet --><servlet-mapping><servlet-name>jsp</servlet-name><url-pattern>*.jsp</url-pattern><url-pattern>*.jspx</url-pattern></servlet-mapping>

由以上配置文件也可以看出,如果是jsp文件的请求,回交给Jsp Servlert,其他的都交给Default Servlet进行处理。

(3)tomcat内部处理流程

在这里简单介绍一下处理流程就好,便于理解后续漏洞的分析。

  1. 用户点击网页内容,请求被发送到本机端口8080,被Connector获得(Connector中的Processor用于封装Request,Adapter用于将封装好的Request交给Container)。
  2. Connector把该请求交给Container中的Engine来处理,并等待Engine的回应。
  3. Engine获得请求localhost/test/index.jsp,匹配所有的虚拟主机Host。
  4. Engine匹配到名为localhost的Host(即使匹配不到也把请求交给该Host处理,因为该Host被定义为该Engine的默认主机),名为localhost的Host获得请求/test/index.jsp,匹配它所拥有的所有的Context。Host匹配到路径为/test的Context(如果匹配不到就把该请求交给路径名为" "的Context去处理)。
  5. path="/test"的Context获得请求/index.jsp,在它的mapping table中寻找出对应的Servlet。Context匹配到URL PATTERN为*.jsp的Servlet,对应于JspServlet类(匹配不到指定Servlet的请求对应DefaultServlet类)。
  6. Wrapper是最底层的容器,负责管理一个Servlet。构造HttpServletRequest对象和HttpServletResponse对象,作为参数调用JspServlet的doGet()或doPost(),执行业务逻辑、数据存储等程序。
  7. Context把执行完之后的HttpServletResponse对象返回给Host。
  8. Host把HttpServletResponse对象返回给Engine。
  9. Engine把HttpServletResponse对象返回Connector。
  10. Connector把HttpServletResponse对象返回给客户Browser。

在介绍漏洞之前还要配一下tomcat源码分析的环境,大家可以根据这篇博文Tomcat源码编译(IDEA)_tomcat 编译_ww0peo的博客-CSDN博客

来进行配置,配置的版本是apache-tomcat-8.5.46,配置好了之后就可以愉快的进行漏洞的代码分析了。

根据上文的Tomcat处理请求流程,请求首先到达Connector,Connector内使用AjpProcessor解析Socket,将Socket中的内容封装到Request中。然后经过一系列的处理,到达Servlet。根据上文我们可以了解到,tomcat默认配置两个Servlet,一个是Default Servlet,另一个是Jsp Servlet,而这这里就分别对应了文件读取漏洞和任意代码执行漏洞。

1.文件读取漏洞

当url未匹配到Servlet时,回给到Default Servlet处理。 

分析org.apache.catalina.servlets.DefaultServlet.java 。请求到达 DefaultServlet 后, 会执行 service() 方法,判断请求的 DispatcherType 是否为 ERROR ,若是则直接调用 doGet() 方法,否则调用父类的 Service() 方法。

 DefaultServlet 的父类为 HttpServlet (javax.servlet.http.HttpServlet.java)。其 service() 方法根据请求方法(method)决定调用 doGet() 或是 doPost() ,代码如下。此 处我们是读取文件,使用的是 doGet 方法。

然后回到子类,查询doGet方法。 通过serveResource方法来获取资源。

 在serveResource方法中,它定义了我们获取资源的路径

跟踪进去,

 我们发现了三个重要的参数,

INCLUDE_REQUEST_URI,   INCLUDE_PATH_INFO,   INCLUDE_SERVLET_PATH

 发现这三个参数可以控制我们请求资源的路径,继续跟踪,发现这三个属性都有默认值

static final String INCLUDE_REQUEST_URI = "javax.servlet.include.request_uri";
static final String INCLUDE_CONTEXT_PATH = "javax.servlet.include.context_path";
static final String INCLUDE_PATH_INFO = "javax.servlet.include.path_info";

 这里就是我们利用的重点,我们可以通过发送数据包来伪造这几个默认值,从而控制路径,到后面我会用wireshark抓包看一下。

由上面截图中的代码可知,当 request 中 javax.servlet.include.request_uri 不为空时,将 request 的 javax.servlet.include.path_info 与 javax.servlet.include.servlet_path 分别赋值给 path_info 和 servlet_path。 而后只要 path_info 与 servlet_path 的值不为空,就将 path_info 与 servlet_path 拼接起来赋值给 result。

回到serveResource 方法,获取到路径后创建资源的实例并且获取资源。

 跟踪进去getResource方法,(位于 org.apache.catalina.webresources.StandardRoot.java中)

 我们发现上述代码调用了validate进行判断,继续跟着

在这里有主要用了normalize方法来进行校验,其主要功能如下:

1.判断path中是否有'\\',有就转换成'/';

2.判断path是否以'/'开头,若不是则在前面加上'/';

3.判断path是否以'/.'或'/..'结尾,若是则结尾加一个'/';

4.判断path是否含有'//',有则替换成'/';

5.判断path是否含有'/./',若有直接截断并重新拼接,如'/./abc'变为'/abc'

6.判断path是否含有'/../',若有则直接返回null;

首先补充一个知识,当我们访问tomcat时,它会默认访问取webapps下的默认文件,而在这里对相对路径的符号进行了限制,所以我们利用该漏洞只能访问webapps下的文件。

在验证完成之后 ,会继续将资源返回到输出流中,再交由tomcat的内部进行处理。

至此利用过程结束,而我们可以通过刚才在 DefaultServlet 的 getRelativePath() 中说过的三个参数进行访问资源路径的控制。而我们首先还需要让请求到达 DefaultServlet ,需要让请求的 url 为 “/asdf” ,此处的 asdf 为随机字符串,目的是让 Tomcat 找不到 webapps 下的至指定文件,从而请求会到 Tomcat 的默认目录。

若是想请求 webapps 目 录下的其他目录,则可以设置为 ‘/指定目录/asdf’,如’’/manager/asdf’。会访问到webapps/manager/下的目录,具体的文件看我们构造的参数。

如要访问”webapps/ROOT/WEB-INF/web.xml”,首先将请求 url 为’/asdf’,再将 javax.servlet.include.request_uri 值设置为’/‘(非空), javax.servlet.include.servlet_path 的值设置为 ‘/‘,javax.servlet.include.path_info 的值设置为 ‘WEB-INF/web.xml’。

poc的下载地址为GitHub - YDHCUI/CNVD-2020-10487-Tomcat-Ajp-lfi: Tomcat-Ajp协议文件读取漏洞

但是在python3.7.9的版本下运行会报错,这里我修改了poc,修改的poc放在了tomcat_Ghostcat/ at main · fengdianxiong/tomcat_Ghostcat · GitHub 上,大家可以自取。

成功获取文件 。

我们再来看一下对应的数据包:

发现在poc的数据包中携带了我们上面三个重要的参数,从而实现文件读取。

2.任意代码执行漏洞

这个漏洞稍微有一点点鸡肋,需要提前先自己上传木马文件,然后利用文件包含漏洞来实现代码执行。

由上文可知,我们知道当请求jsp文件时,会走Jsp Servlet来进行处理。

首先来到JspServlert.java(位于org.apache.jasper.servlet.JspServlet.java),经过service方法处理,由标红部分可以看出,jspUri由servlet_path和path_info拼接而成。

然后进入到serviceJspFile方法中,方法利用 jspUri 生成 JspServletWrapper 实例,再到调用实例的 service() 方法。

获取到对应的Servlet

调用该Servlet的service方法

请求执行,从而造成文件包含。

利用过程简单就是,我们通过传入 servlet_path 和 path_info ,让其去将指定的文件作为 jsp 文件处理,Tomcat 会将根据指定的文件生成 java 代码并编译成 class ,而后加载 class 类实例化一个 servlet 并执行,从而造成文件包含。

在复现时,要修改一下poc的参数,然后在webapps目录下的ROOT下(apache-tomcat-8.5.46-source\catalina-home\webapps\ROOT),自己创建一个1.txt,

内容为

<%Runtime.getRuntime().exec("calc.exe");%>

然后再次利用poc,弹出计算器。

三、修复建议

(1)官方网站下载新版本进行升级。

(2)直接关闭 AJP Connector,或将其监听地址改为仅监听本机 localhost。

(3)若需使用 Tomcat AJP 协议,可根据使用版本配置协议属性设置认证凭证。

这篇关于幽灵猫 Tomcat文件读取/任意文件包含漏洞(CVE-2020-1938 / CNVD-2020-10487)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/664620

相关文章

Linux下利用select实现串口数据读取过程

《Linux下利用select实现串口数据读取过程》文章介绍Linux中使用select、poll或epoll实现串口数据读取,通过I/O多路复用机制在数据到达时触发读取,避免持续轮询,示例代码展示设... 目录示例代码(使用select实现)代码解释总结在 linux 系统里,我们可以借助 select、

全网最全Tomcat完全卸载重装教程小结

《全网最全Tomcat完全卸载重装教程小结》windows系统卸载Tomcat重新通过ZIP方式安装Tomcat,优点是灵活可控,适合开发者自定义配置,手动配置环境变量后,可通过命令行快速启动和管理... 目录一、完全卸载Tomcat1. 停止Tomcat服务2. 通过控制面板卸载3. 手动删除残留文件4.

C#实现SHP文件读取与地图显示的完整教程

《C#实现SHP文件读取与地图显示的完整教程》在地理信息系统(GIS)开发中,SHP文件是一种常见的矢量数据格式,本文将详细介绍如何使用C#读取SHP文件并实现地图显示功能,包括坐标转换、图形渲染、平... 目录概述功能特点核心代码解析1. 文件读取与初始化2. 坐标转换3. 图形绘制4. 地图交互功能缩放

java读取excel文件为base64实现方式

《java读取excel文件为base64实现方式》文章介绍使用ApachePOI和EasyExcel处理Excel文件并转换为Base64的方法,强调EasyExcel适合大文件且内存占用低,需注意... 目录使用 Apache POI 读取 Excel 并转换为 Base64使用 EasyExcel 处

mybatis-plus如何根据任意字段saveOrUpdateBatch

《mybatis-plus如何根据任意字段saveOrUpdateBatch》MyBatisPlussaveOrUpdateBatch默认按主键判断操作类型,若需按其他唯一字段(如agentId、pe... 目录使用场景方法源码方法改造首先在service层定义接口service层接口实现总结使用场景my

修复已被利用的高危漏洞! macOS Sequoia 15.6.1发布

《修复已被利用的高危漏洞!macOSSequoia15.6.1发布》苹果公司于今日发布了macOSSequoia15.6.1更新,这是去年9月推出的macOSSequoia操作... MACOS Sequoia 15.6.1 正式发布!此次更新修复了一个已被黑客利用的严重安全漏洞,并解决了部分中文用户反馈的

使用Java读取本地文件并转换为MultipartFile对象的方法

《使用Java读取本地文件并转换为MultipartFile对象的方法》在许多JavaWeb应用中,我们经常会遇到将本地文件上传至服务器或其他系统的需求,在这种场景下,MultipartFile对象非... 目录1. 基本需求2. 自定义 MultipartFile 类3. 实现代码4. 代码解析5. 自定

MySQL 数据库表操作完全指南:创建、读取、更新与删除实战

《MySQL数据库表操作完全指南:创建、读取、更新与删除实战》本文系统讲解MySQL表的增删查改(CURD)操作,涵盖创建、更新、查询、删除及插入查询结果,也是贯穿各类项目开发全流程的基础数据交互原... 目录mysql系列前言一、Create(创建)并插入数据1.1 单行数据 + 全列插入1.2 多行数据

IDEA中配置Tomcat全过程

《IDEA中配置Tomcat全过程》文章介绍了在IDEA中配置Tomcat的六步流程,包括添加服务器、配置部署选项、设置应用服务器及启动,并提及Maven依赖可能因约定大于配置导致问题,需检查依赖版本... 目录第一步第二步第三步第四步第五步第六步总结第一步选择这个方框第二步选择+号,找到Tomca

SpringBoot多环境配置数据读取方式

《SpringBoot多环境配置数据读取方式》SpringBoot通过环境隔离机制,支持properties/yaml/yml多格式配置,结合@Value、Environment和@Configura... 目录一、多环境配置的核心思路二、3种配置文件格式详解2.1 properties格式(传统格式)1.