Tomcat Ajp协议漏洞
Tomcat主要是提供Servlet/JSP容器,静态资源的处理速度以及Web服务管理功能方面不如其他专业的HTTP服务器,所以使用效率和性能更高二进制TCP传输协议ajp协议以供其他web服务器进行反向代理或者用于集群,而8009端口的ajp服务默认在公网开启,通过ajp协议可以控制request对象的某些Attribute属性从而造成webapps目录下的任意文件读取/包含
IDEA调试Tomcat
github下载存在漏洞的版本https://github.com/apache/tomcat/releases/tag/9.0.19
解压源码后,在源码根目录新建 home 文件夹,把 conf 文件夹和 webapps 文件夹移动到 home 文件夹里,然后在源码根目录新建 pom.xml 文件(原来为 Ant 工程,这里把它改为 Maven 工程)
1 |
|

用IDEA导入

将java文件夹标记为Sources Root,test标记为Test Sources Root

运行org.apache.catalina.startup.Bootstrap#main方法

找不到trailers,把 home/webapps/examples/WEB-INF/classes/trailers 目录拷贝到 test 目录下

再次运行,找不到CookieFilter,把 home/webapps/examples/WEB-INF/classes/util/CookieFilter.java 文件拷贝到 test/util 目录下

conf/server.xml 找不到,设置下 jvm 参数(就是指定之前创建的 home 目录)
-Dcatalina.home=

访问网页报错

删除webapps /examples文件夹
编辑org.apache.catalina.startup.ContextConfig#configureStart方法,添加初始化 JSP 解析器的代码
context.addServletContainerInitializer(new JasperInitializer(), null);


Tomcat Ajp协议
Tomcat默认的conf/server.xml中配置了2个Connector
一个为8080端口的HTTP协议,另外一个就是8009端口的AJP协议

Tomcat最主要的功能是提供Servlet/JSP容器,尽管它也可以作为独立的Java Web服务器,但它在对静态资源(如HTML文件或图像文件)的处理速度,以及提供的Web服务器管理功能方面都不如其他专业的HTTP服务器,如IIS和Apache的服务器。因此在实际应用中,常常把Tomcat的与其他HTTP服务器集成。对于不支持的Servlet/JSP的HTTP服务器,可以通过的Tomcat服务器来运行的Servlet/JSP组件。而Tomcat和其他服务器的集成,就是通过ajp协议来完成的
ajp是一个二进制的TCP传输协议,相比HTTP这种纯文本的协议来说,效率和性能更高,也做了很多优化。显然,浏览器并不能直接支持ajp协议,只支持HTTP协议。所以通常是通过Apache的proxy_ajp模块进行反向代理,暴露成http协议给客户端访问

Debug运行Tomcat,nc 尝试连接可以看到8009端口开放

漏洞分析
文件读取
Tomcat在处理ajp请求的时候调用org.apache.coyote.ajp.AjpProcessor#prepareRequest来解析一些请求头
在该处下断点,并利用poc发送ajp请求

org/apache/coyote/ajp/AjpProcessor.java

当ajp数据包的头设置为SC_REQ_ATTRIBUTE
时,Connector会紧接着读取变量n
(属性名)和v
(值),当n
不是SC_A_REQ_LOCAL_ADDR
、SC_A_REQ_REMOTE_PORT
、SC_A_SSL_PROTOCOL
时,就会用v
来赋值属性n



接着,service()
方法将修改过的request代入后面的调用

poc请求的url为/asdf

当请求的uri无法匹配其他servlet时会由DefaultServlet处理,其中的调用流程如下

在org.apache.catalina.servlets.DefaultServlet
中,当我们的请求声明的是GET方法时,存在调用service()->doGet()->serveResource()
org.apache.catalina.servlets.DefaultServlet#serveResource

org.apache.catalina.servlets.DefaultServlet#getRelativePath

serveResource()
方法获得path后会将path
带入到getResource
方法中造成任意文件读取

之后的代码逻辑是把通过path获取的资源序列化输出,因此客户端再按照AJP协议解析数据包就能得到文件内容
文件包含
Tomcat默认将jsp/jspx结尾的请求交给org.apache.jasper.servlet.JspServlet
处理
修改一下POC,将请求url改为jsp结尾

WEB-INF下新建一个文件


在org.apache.jasper.servlet.JspServlet#service下断点

这里同样会获取javax.servlet.include.path_info、javax.servlet.include.servlet_path这两个属性(可以通过ajp协议控制这两个属性)
将这两个属性 对应的值拼接到jspURi变量中,最后交给serviceJspFile方法处理


可读路径
文件读取
该漏洞只能读取webapps目录下的文件,不能通过目录穿越读取其他路径
尝试在paylaod中加入../

步入DefaultServlet中调用的getResource

org.apache.catalina.webresources.StandardRoot#getResource


org.apache.catalina.webresources.StandardRoot#validate

org.apache.tomcat.util.http.RequestUtil#normalize(java.lang.String, boolean)

其中检测到了/../就会return null
在org/apache/catalina/webresources/StandardRoot抛出异常

文件包含
先修改一下POC


跟入
org.apache.jasper.servlet.JspServlet#serviceJspFile


跟着jspUri变量,会发现后面就进入了和文件读取一样的检测逻辑

跨webapp读取/包含
目前都是读取默认的ROOT下的文件,如果想跨webapp读取,可以修改POC中的请求url

创建test目录并写入文件


修改POC,url中换成test目录,并且后面跟随随机字符(当请求的uri无法匹配其他servlet时才会由DefaultServlet处理)


修改POC,包含文件并执行


修复方案
升级最新版本
禁用AJP协议
删除或注释conf/server.xml中的
<Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />
配置secret来设置AJP协议的认证凭证
<Connector port="8009"protocol="AJP/1.3" redirectPort="8443"address="YOUR_TOMCAT_IP_ADDRESS" secret="YOUR_TOMCAT_AJP_SECRET"/>
参考
CNVD-2020-10487-Tomcat-Ajp-lfi