一步一步学习如何编写自己的poc
概述
主要参考自 https://www.chaitin.cn/zh/ghostcat
膜一波长亭的大师傅
AJP协议
AJP13是定向包协议。因为性能原因,使用二进制格式来传输可读性文本。WEB服务器通过 TCP连接和SERVLET容器连接。为了减少进程生成 socket的花费,WEB服务器和SERVLET容器之间尝试保持持久性的TCP连接,对多个请求/回复循环重用一个连接。一旦连接分配给一个特定的请 求,在请求处理循环结束之前不会在分配。换句话说,在连接上,请求不是多元的。这个是连接两端的编码变得容易,虽然这导致在一时刻会有很多连接。
Tomcat默认开启ajp connector监听8009端口, 而其设计上存在缺陷, 在一定的利用下, 可以导致Web目录下的文件读取和文件包含, 如果还可以任意文件上传, 则会导致RCE
影响版本
Apache Tomcat 9.x < 9.0.31
Apache Tomcat 8.x < 8.5.51
Apache Tomcat 7.x < 7.0.100
Apache Tomcat 6.x
利用条件
- 开启AJP connector
- AJP connector监听的端口(默认为8009)可以被外网访问
修复建议
如果没有使用AJP协议
直接更新最新版本即可
如果不能直接更新, 可以禁止AJP的默认开启, 或将其监听地址改为localhost
如果使用了AJP协议
直接升级或为 AJP Connector 配置 secret 来设置 AJP 协议认证凭证。
Q&A
这部分摘抄自 @phithon 师傅的知识星球
是否可以跨目录读取到tomcat管理员密码,
/etc/passwd
等文件?不可以, 只能读取和包含webapps目录下的文件
是否可以跨APP读取Tomcat下其他应用的文件?
可以, 需要修改poc
是文件包含还是文件读取漏洞?
漏洞有两种利用方式, 可以文件包含或者文件读取
docker拉取的tomcat无法利用成功?
因为从2020年开始docker中的tomcat默认不开启examples, ROOT等应用, webapps默认目录为空, 可以使用 https://github.com/vulhub/vulhub/tree/master/tomcat/CVE-2020-1938 来进行测试
为什么利用不成功?
很可能是你的poc写的不全
环境搭建
主要参考的是
https://blog.csdn.net/u013268035/article/details/81349341
一步步跟过去就可以搭建成功了
然后这里可以开启debug
利用的脚本用的是
https://github.com/hypn0s/AJPy
初步分析
主要参考
https://www.guildhab.top/?p=2406
Tomcat-Ajp协议漏洞分析利用(CVE-2020-1938)
认识一个漏洞最快的方式, 就是通过github提交的记录来分析漏洞点
我们看一下tomat7.0.100的提交记录
https://github.com/apache/tomcat/commits/7.0.100
从底往上看
首先是默认禁止AJP connector的开启
第二步是改监听地址为localhost
这和我们前面提到的修复方法是一致的
第三步是设置凭证, 如果没有设置凭证或凭证为空则不启动AJP connector
第四步添加一个属性, 如果请求带有无法识别的参数则返回403
第五步更改了ajp的注册方式(依然是默认注释)
我们大概就可以了解到这个漏洞的一部分情况
- 外部可以访问AJP connector的监听端口
- 携带部分属性使得可利用
具体的还是要看代码和poc来观察
调试分析
https://www.guildhab.top/?p=2406 这位师傅分析的十分详细, 主要是从DefaultServlet
方面讲的, 我们就从JspServlet
角度来简单聊聊, 原理也差不多
我们打开debug模式, 打个断点在org/apache/coyote/ajp/AjpProcessor.java#167
然后运行poc
python .\tomcat.py read_file --webapp=manager /WEB-INF/web.xml 127.0.0.1 |
就可以发现成功打断了, F7
单步调试
来到这里
可以看到AJP协议其实是被当成GET解析了
然后在这里进入了一个switch循环, 此时的attributeCode
是10
, 对应的case的含义为
AJP connector没有预定义的属性 |
进去之后, 可以看到主要的点在这里
由于我们加进去的header头不在匹配列表中, 到最后一个else
, 直接注册为request
的attribute
随后注入的是请求的文件路径
总共注入的有三个属性, 通过抓包我们也可以直接看到
然后继续F7
此时调用CoyoteAdapter
来执行请求, adapter
是用来连接connector
和container
的组件
我们可以在中间看到, URI会经过normalize
的过滤, 因此无法进行目录穿越, 会被限制在webapps
下
继续跟到JspServlet
里面
当无法获取到对应的jspuri
时, 就会去获取javax.servlet.include.servlet_path
, 而这个值我们设定为/
接下来会获取javax.servlet.include.path_info
也就是读取的文件, 然后到这里
就开始读文件了, 上一张调用链 https://www.freebuf.com/vuls/228108.html
看一下脚本的返回
成功, 我们来小结一下
需要传递三个参数
javax.servlet.include.request_uri
javax.servlet.include.path_info
javax.servlet.include.servlet_path其中第二个是我们读取的文件名
由于不是ajp相关的头, tomcat会把他们注册到
request
里面然后访问一个不存在的文件
xxx.jsp
程序会交给
jspServlet
执行, 由于不存在对应的jspUri, 会取出我们的三个参数并进行一定的处理, 同时过滤../
使用
getResource
读取文件并返回
那么我们如果已经上传了一个jsp马, 当这个马传递给jspServlet
, 是不是就会直接执行了呢
先写一个马
<%@page import="java.lang.*"%> |
然后放到
home/webapps/manager/ |
下, 读取即可执行
不过不知道为啥没有弹回shell, 猜测和win有些关系
当然, 由于是文件包含, 并不需要用jsp
后缀, 例如我们改为1.txt
可以看到依然执行了, 因此是任意文件包含, 那如果想读其他的webapps呢
例如想读ROOT
, 改一下前面的参数即可
--webapp=ROOT |
另外我们可以观察到, 前面我们进入的是jspServlet
, 这个是由我们访问的后缀确定的, 那么如果是其他的后缀呢?
我们改为html
可以看到, 现在只能读取而不能执行了, 原理的话和前面差不多, 就不多说了
编写poc
编写poc的主要问题应该是在于如何发出一个ajp请求, 直接用requests库是不行的, 并没有一个ajp
方法
所以我们要自己来写一个方法
看了一眼需要先了解一下ajp的规范, 先留个坑23333
具体的格式可以参考 https://blog.51cto.com/guojuanjun/688559
作者: cjm00n
地址: https://cjm00n.top/Web/cve-2020-1938-learning.html
版权声明: 除特别说明外,所有文章均采用 CC BY 4.0 许可协议,转载请先取得同意。
评论