简介
AJP是Apache提供的完成与其它服务器通讯的一种协议,用的是二进制传输,比HTTP文本传输有更高的效率。
请求传输到tomcat的数据一般先经由tomcat中的连接器(connector),tomcat connector组件负责解析客户端发来的报文并封装成request对象,还负责组装响应回客户端的报文将其封装成Response对象。
HTTP协议的连接器默认监听8080端口,负责建立HTTP连接。通过浏览器访问Tomcat服务器的web应用时使用的就是HTTP Connector。
AJP协议的连接器默认监听8009端口,负责和其他HTTP服务器建立连接,在tomcat与其他HTTP服务器连接时用的就是,AJP Connector。
AJP与HTTP的配置在conf/server.xml中
漏洞利用流程跟踪:
环境准备:
首先下载tomcat存在AJP协议漏洞的版本源码文件并配置用idea打开项目,具体步骤可参考Tomcat源码编译,如果访问tomcat首页出现500错误可在org.apache.catalina.startup.ContextConfig 类中增加如下一行初始化jsp解析。
poc(https://github.com/YDHCUI/CNVD-2020-10487-Tomcat-Ajp-lfi)
流程分析:
AJP协议的请求在tomcat中内的请求流程与HTTP相似,主要走向为AjpProcessor类->service()->prepareRequest()。而AJP协议漏洞产生是因为tomcat对ajp传递过来的数据处理存在问题,导致我们可以控制”javax.servlet.include.request_uri”,”javax.servlet.include.path_info”,”javax.servlet.include.servlet_path”,从而读取文件或者文件包含。
因此对流程分析时首先从AjpProcessor类的service()处断点开始debug然后在kali中运行poc发起ajp请求
在poc中我们设置了AJP协议的三个参数
而在tomcat对ajp请求处理流程中设置参数的位置在AjpProcessor.AjpProcessor()方法中
AjpProcessor.AjpProcessor()方法调用了request.setAttribute()方法来设置参数值,这次的AJP请求中该方法会被循环执行三次,这里我们可以继续跟入request.setAttribute()方法。
如上图requests.setAttribute()方法被循环三次的过程中,poc构造的三个参数会被存入attributes这个hashmap,这里就是漏洞触发的第一个点。
在继续后面servlet的分析之前可以复现一遍整个过程用wireshark抓取数据包观察
在请求中有四个重要参数,我们请求的URI是”/asdf”这是一个在后台中并不存在的servlet。Tomcat中有两个默认定义的servlet,一个是Defaultservlet另一个是Jspservlet,当URI无法匹配任何servlet时,会由Defaultservlet来处理。而DefaultServlet处理流程是DefaultServlet类->service()->doGet()/doPost(),这里使用的poc中默认get请求,所以直接在DfaultServlet类下的doGet()方法处断点然后执行poc发送请求。
doGet方法调用了saveResource()方法,跟进saveResource()方法发现serveResource()方法又调用了getRelativePath()方法来对请求的路径进行拼接
跟入getRelativePath()方法可以发现,这里就是将我们传入的path_info 、servlet_path 进行复制、拼接的地方,其中request_uri用来做判断,如果传递进来的request_uri为空则漏洞利用失败。
再来观察getResource方法
getResource调用了非法路径检测函数vaildate()并传入了path值来判断路径是否符合限制条件
在vaildate()方法中定义了传入path的限制,因此文件读取操作中不能由/../这形式
最后回到getResource()方法开始文件读取漏洞利用。
RCE
之前提到过tomcat默认的servlet有两个,触发文件读取的是Defaultservlet,当URI以.jsp结尾时默认匹配Jspservlet
1.根据JSP文件生成对应Servlet的Java代码(JSP文件生成类的父类我org.apache.jasper.runtime.HttpJspBase——实现了Servlet接口)
2.将Java代码编译为Java类。
3.构造Servlet类实例并且执行请求。
其实本质核心就是通过JspServlet来执行我们想要访问的.jsp文件
所以想要RCE的前提就是,先要想办法将写有自己想要执行的命令的文件(可以是任意文件后缀,甚至没有后缀)上传到webapps的目录下,才能访问该文件然后通过JSP模板的解析造成RCE。
参考文章
1、https://mp.weixin.qq.com/s/hH0dpRWml0Rt7FxFOsWcMg
2、https://xz.aliyun.com/t/7683
3、https://blog.csdn.net/qq_35262405/article/details/101780644