长亭百川云 - 文章详情

CVE-2020-1938:Tomcat AJP协议文件包含漏洞分析

回忆飘如雪

49

2024-07-14

PS: 文章仅用于研究漏洞原理,促进更好的防御,禁止用于非法用途,否则后果自负!!!

 0x01 漏洞简介 

Tomcat根据默认配置(conf/server.xml)启动两个连接器。一个是HTTP Connector默认监听8080端口处理HTTP请求,一个AJP connector默认8009端口处理AJP请求。Tomcat处理两个协议请求区别并不大,AJP协议相当于HTTP协议的二进制优化版。

**本次漏洞出现在通过设置AJP请求属性,可控制AJP连接器封装的request对象的属性,最终导致文件包含可以任意文件读取和代码执行。**下面我们以Tomcat 8.5.47来具体分析。

 0x02 漏洞分析 

当我们向Tomcat发送AJP请求时,请求会被org.apache.coyote.ajp.AjpProcessor,AjpProcessor调用prepareRequest方法读取AJP请求中的信息来设置request属性。

由于没有任何过滤,我们可以给request设置任何属性和值。本次漏洞与如下三个属性有关,为了方便后续描述统一简称为“三个include属性”。

  • javax.servlet.include.request_uri

  • javax.servlet.include.path_info

  • javax.servlet.include.servlet_path

最终会将封装好的request丢给Servlet容器Catalina处理,之后就和HTTP消息的处理一样,按照Servlet映射走。

2.1 任意文件读取

任意文件读取问题出现在org.apache.catalina.servlets.DefaultServlet这个Servlet。现在假设我们发出一个请求内容如下的AJP请求

`RequestUri:/docs/test.jpg``javax.servlet.include.request_uri: /``javax.servlet.include.path_info: WEB-INF/web.xml``javax.servlet.include.servlet_path: /`

通过查看Servlet映射规则(conf/web.xml)知道,请求会走默认的DefaultServlet。

`<servlet>``<servlet-name>default</servlet-name>``<servlet-class>org.apache.catalina.servlets.DefaultServlet</servlet-class>``<init-param>``<param-name>debug</param-name>``<param-value>0</param-value>``</init-param>``<init-param>``<param-name>listings</param-name>``<param-value>false</param-value>``</init-param>``<load-on-startup>1</load-on-startup>``</servlet>``...``<!-- The mapping for the default servlet -->``<servlet-mapping>``<servlet-name>default</servlet-name>``<url-pattern>/</url-pattern>``</servlet-mapping>``...`

会交给org.apache.catalina.servlets.DefaultServlet的doGet方法处理。doGet会调用ServeResource方法进行具体的资源读取操作。首先它会调用 getRelativePath方法获取要读取资源的相对路径,这里注意它是本次任意读取漏洞的关键,我们先往下看后续再细说它。通过getResources方法就可以获取到了对应路径的Web资源对象了。

最后资源对象的内容随着resourceBody被写入了ostream流对象中返回给客户端。

接下来我们来看漏洞真正核心,org.apache.catalina.servlets.DefaultServlet类的getRelativePath(),它负责获取资源的相对路径。由于我们AJP请求设置javax.servlet.include.request_uri属性值为/不为null。故资源

的相对路径构造如下:

`= javax.servlet.include.path_info + javax.servlet.include.path_info``= / + WEB-INF/web.xml``= /WEB-INF/web.xml`

这就导致我们虽然请求的是/docs/test.jpg文件内容,而实际上返回了/docs/WEB-INF/web.xml文件的内容。

至此大家可能有两个疑问

问题1:为何Tomcat处理HTTP协议不存在该问题?

答:因为在HTTP请求中,我们无法控制request对象三个include属性的值,而在AJP请求中可以。

问题2:为何无法跳出webapps目录读文件呢?

DefaultServlet在读取资源时

会调用org.apache.tomcat.util.http.RequestUtil工具类中的normalize方法来对路径进行校验,如果存在./或../则会返回null,最终会抛出一个非法路径的异常终止文件读取操作。

2.2 任意代码执行

任意代码执行问题出现在org.apache.jasper.servlet.JspServlet这个servlet,假设我们发出一个请求内容如下的AJP请求,让Tomcat执行/docs/test.jsp,但实际上它会将code.txt当成jsp来解析执行。

`RequestUri:/docs/test.jsp``javax.servlet.include.request_uri: /``javax.servlet.include.path_info: code.txt``javax.servlet.include.servlet_path: /`

code.txt内容如下:

<%Runtime.getRuntime().exec("/Applications/Calculator.app/Contents/MacOS/Calculator");%>

按照映射规则,我们的请求会被org.apache.jasper.servlet.JspServlet进行处理。

`<servlet>``<servlet-name>jsp</servlet-name>``<servlet-class>org.apache.jasper.servlet.JspServlet</servlet-class>``<init-param>``<param-name>fork</param-name>``<param-value>false</param-value>``</init-param>``<init-param>``<param-name>xpoweredBy</param-name>``<param-value>false</param-value>``</init-param>``<load-on-startup>3</load-on-startup>``</servlet>``...``<!-- 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>`

由于javax.servlet.include.servlet_path值为/不为null,所以根据代码逻辑我们jsp文件的路径为:

`jspUri = javax.servlet.include.servlet_path + javax.servlet.include.path_info``jspUri = / + code.txt``jspUri = /code.txt`

可见jspUri是客户端可控。

由我们控制的jspUri被封装成了一个JspServletWrapper添加到了Jsp运行上下文JspRuntimeContext中。最后wrapper.service()会编译code.txt,并执行它的_jspService()方法来处理当前请求,我们的代码被执行。

综上整个过程就清晰了,简而言之就是我们发送AJP请求,请求的是/docs/test.jsp这个jsp,但是由于那三个include属性可控,我们可以将test.jsp对应的服务器脚本文件改为了code.txt。导致tomcat把我们的code.txt当jsp文件编译运行,导致代码执行。

最后给大家提两个问题:

问题1: 请求的**/docs/test.jsp****需要在web目录下真是存在么?**

答:不需要,我们只是为了让请求路径命中org.apache.catalina.servlets.DefaultServlet这个Servlet的匹配规则。

问题2: 如果tomcat不解析任何jsp,jspx等后缀,或者以它们为view的模板,还能触发漏洞么?如果可以又该如何触发?

**PS:**这个问题是一个师傅留给我的,觉得很有意思,分享给大家思考,有想法的可以留言讨论。

 0x03 漏洞修复 

Tomcat在8.5.51版本做了如下修复 :

  1. 默认不开启AJP

  2. 默认只监听本地ip

  3. 强制设置认证secret

  4. 代码层面主要在AjpProcessor类的prepareRequest方法封装requst对象时采用了白名单,只添加已知属性。这样三个include属性不再被客户端控制,漏洞修复。

0x04 参考文章 

相关推荐
关注或联系我们
添加百川云公众号,移动管理云安全产品
咨询热线:
4000-327-707
百川公众号
百川公众号
百川云客服
百川云客服

Copyright ©2024 北京长亭科技有限公司
icon
京ICP备 2024055124号-2