前言
跟各类大拿比,写的很晚,尽量详细的写下此漏洞和挖掘过程中的思维CVE-2020-1938,官方威胁公告:CNVD官方说明
AJP协议
首先得了解下AJP协议,避免在深入挖掘此漏洞时遇到坑比如我,AJP说白了就是一种传输层协议,由tomcat出品,显然是一种面向连接,效率较高的传输协议,下面这个图百度的,基本就这么回事了。具体的协议内容见(下面在分析代码的时候会遇到各种getbytes的枚举,其中各数字代表应用层向下封装的头部分,物数网传会表应不多介绍了哈,了解下OSI7层模型):官方AJP协议介绍文档
/conf/server.xml中可配置connector,这个也是官方临时修复建议需要注释掉的点也就是关闭8009端口,关闭了AJP协议,自然就没有此漏洞了,此漏洞需要借助ajp协议进行攻击。
漏洞成因
tomcat7.0.99举例:下载位置
首先看二进制的输入点,定位到ajp中调用socket的点,位置为/org/apache/coyote/ajp目录,索引socket关键字。
先看下安恒给的点AjpProcessor.java
预请求的prepareRequest()方法来自于父类AbstractAjpProcessor,找下
关键点在如下代码中,在这个之前呢,贴几个图,当然可以下一个tomcat版本自己看,各版本有细微差异,比如枚举的值有的直接就是赋值硬编码,可能是后来改进了。
下面的代码加上详细注释方便新手理解,因为一般相对了解javaweb的人都比较难看懂。
// Decode extra attributes
boolean secret = false;
//定义一个byte类型的参数attributecode
byte attributeCode;
//while()!=结构,当ATBcode=requestHM.getbyte(),atbcode=请求头信息类型转换的byte,当其不等于255(这个对应Constants.SC_A_ARE_DONE的值0xFF)时,执行下面的switch,case语句,这里是个恒true的循环。
while ((attributeCode = requestHeaderMessage.getByte())
!= Constants.SC_A_ARE_DONE) {
switch (attributeCode) {
//当case到Constants.SC_A_REQ_ATTRIBUTE时,这个枚举的值是10,在AJP协议封装里,代表attribute类型。
case Constants.SC_A_REQ_ATTRIBUTE :
//tmpMB在上面有定义,提取byte[]数组里面的子byte,再tostring转换为string类型,有同学可能不能理解为啥定义两个相同的n和v参数,这里是为了在下面一个进行判断一个进行赋值,tomcat的开发人员都是大佬,这样写可以避免参数的混乱。
requestHeaderMessage.getBytes(tmpMB);
String n = tmpMB.toString();
requestHeaderMessage.getBytes(tmpMB);
String v = tmpMB.toString();
/*
* AJP13 misses to forward the local IP address and the
* remote port. Allow the AJP connector to add this info via
* private request attributes.
* We will accept the forwarded data and remove it from the
* public list of request attributes.
*/
//赋值IP给request
if(n.equals(Constants.SC_A_REQ_LOCAL_ADDR)) {
request.localAddr().setString(v);
//赋值端口给request
} else if(n.equals(Constants.SC_A_REQ_REMOTE_PORT)) {
try {
request.setRemotePort(Integer.parseInt(v));
} catch (NumberFormatException nfe) {
// Ignore invalid value
}
//赋值协议https协议key给request
} else if(n.equals(Constants.SC_A_SSL_PROTOCOL)) {
request.setAttribute(SSLSupport.PROTOCOL_VERSION_KEY, v);
//v的值直接setAttribute给request的属性。
} else {
request.setAttribute(n, v );
上面的代码是核心漏洞输入点,既然找到了输入点,那么就要找AJP-REQUEST的输出点和REQUEST-RESPONSE的点。
接下来,就要利用到tomcat的内核连接器与容器的桥梁——CoyoteAdapter类了(百度。。。。。),下面的图是子类AjpNioProcessor的点,关于Adapter类如何处理request的其实不重要,很多人可能会苦读这块内容导致被绕晕。
接着就到了HttpServlet处理请求的点
在defaultservlet中包含获取的属性
到这里基本漏洞的原理就差不多了,最后就是注意一下file中会对path进行限制跨目录
至于jsp文件的处理基本也差不多,核心漏洞输入点在上文ajp-http中,输出处理不一样罢了。
复现方式可以利用脚本,也可以利用java客户端发送ajp数据包。