Tomcat-Ajp漏洞:我是如何一步步写出POC的?

ApacheTomcat 9.x 
    

二、调试环境搭建

为了观察数据流向、编写poc,我们需要搭建调试环境。 tomcat是开源项目,所以我首先想的是下载源码,在源码基础上调试,这里我下载的版本是9.0.2的代码。调试器选择的是Idea。 我先在网上搜索了下idea 调试 tomcat源码的文章。最后参考了 https://blog.csdn.net/weixin_30631587/article/details/96528373 这篇文章,将pom.xml中tomcat的版本由9.0.14修改成9.0.2。,不过这里我觉得不改也不会影响结果。成功运行tomcat服务。

三、ajp协议学习

到目前为止,我还不知道什么是ajp协议,这个协议是干啥的,但是既然知道此次tomcat漏洞是ajp协议造成的。我们肯定要去了解下这个协议是什么。 Apache官方有说明文档 http://tomcat.apache.org/connectors-doc-archive/jk2/common/AJPv13.html 当然这个文档一眼看上去,是不太容易理解的,于是百度一番 我理解的是,我们要访问tomcat网站有两种方式,一种是通过浏览器直接输入url地址。 另外一种就是通过ajp协议访问。 AJP协议是定向包(面向包)协议,采用二进制形式代替文本形式,以提高性能。 所以我们需要写个ajp的客户端程序用来与tomcat服务器的8009端口进行数据交互。当然我们可以自己从头写一个ajp客户端,前提是我们需要很详细了解ajp协议及其各个字段含义。我并不打算如此,毕竟自己从头写起来还是很费劲的。我先到github上用关键词ajp和ajp client搜了一下,看来白的人运气不会太差,发现已经有别人的ajp-client项目。 我把三个ajp-client都下了下来,经过测试和对比(边调试边测试),最后使用了 https://github.com/espenhw/ajp-client 这个项目,最后poc也是在这个项目上完成的。 这里我们需要说下ajp协议中比较重要的字段。 Forward Request包就是我们要发送给tomcat 8009端口的内容,用来触发漏洞的。该字段中比较重要的字段是attributes,后面调试跟踪的时候,也会发现的,后面再说。

四、调试跟踪

通过参考我们知道,tomcat在接收ajp请求的时候调用org.apache.coyote.ajp.AjpProcessor来处理ajp消息,prepareRequest将ajp里面的内容取出来设置成request对象的Attribute属性。 我们现在AjpProcessor中定位到prepareRequest()函数,并在函数开始出下上断点,在request.setAttribute(n, v )也下上断点。 编写测试代码如下(test_servlet是我自己编写的servlet代码,放在了webapps目录下): 运行程序,程序成功断在了prepareRequest(),继续向下单步执行,期间可以观察一些字段的变化。但是程序并没有进入while循环,自然也不会执行request.setAttribute(n, v )函数。(此时我对attributes还不懂,不知道这个字段的意义),再次跟踪时发现while循环的判断条件中在获取attitudes的值时,返回值为-1。然后又回头重新看了下前一节所说的Forward Request结构,看到了其中的attitudes字段,心想这个字段也许和代码中while循环判断的attitudecode有关。 然后看了看ajp-client中代码对attitudes字段的处理,发现该项目没有处理attitudes字段。就按照自己的理解在AjpClient.java中添加了attitudes处理相关代码。
public List<Pair> headers = new LinkedList<Pair>(); 
public List<Pair> attributes = new LinkedList<Pair>();

添加函数setHeaders()此函数并不重要,添加函数addAttributes()。


在query函数中添加,处理atrribute代码


修改测试代码如下


(test_servlet/xx是一个不存在的地址或映射,只有设置成不存在的地址,代码流程才会进入DefaultServlet)
运行程序,程序成功进入while循环


至于addAttributes参数为什么这样写,我已开始自然也是不知道的,多跟踪调试几次就知道了。
程序执行了request.setAttribute(n, v )函数,接下来就是DefaultServlet的serveResource函数。


跟踪到getRelativePath函数中,


这里就会获取我们设置的attributes值。然后通过resources.getResource(path);判断设置的路径文件是否存在,如果存在则返回文件内容,不存在则报错。
已经成功读到文件。


关于org.apache.jasper.servlet.JspServlet类实现文件包含,这里就不分析了,有兴趣的如法炮制即可。

五、编写poc

如何编写poc就不在讲了,上面的测试代码稍微修改,就是poc了。

六、总结

总的来说,该漏洞的利用并不是很难,即使你不懂ajp协议,有些参考,稍微花点时间,还是能够独立写出poc的,当然我实际调试的时候也没有文章中那么顺的,要有耐心,多调试几次就好。修复的话,如果不需要ajp的,可以把配置文件中的8009配置关闭,或者更新到最新版本。

参考:

https://blog.csdn.net/u012206617/article/details/104416626/

https://blog.csdn.net/kalman2008/article/details/24487703

https://blog.csdn.net/jeikerxiao/article/details/82745516

http://tomcat.apache.org/connectors-doc-archive/jk2/common/AJPv13.html

*本文原创作者:MrCoding,本文属于FreeBuf原创奖励计划,未经许可禁止转载