CVE-2020-1938漏洞分析

前言

跟各类大拿比,写的很晚,尽量详细的写下此漏洞和挖掘过程中的思维CVE-2020-1938,官方威胁公告:CNVD官方说明

AJP协议

首先得了解下AJP协议,避免在深入挖掘此漏洞时遇到坑比如我,AJP说白了就是一种传输层协议,由tomcat出品,显然是一种面向连接,效率较高的传输协议,下面这个图百度的,基本就这么回事了。具体的协议内容见(下面在分析代码的时候会遇到各种getbytes的枚举,其中各数字代表应用层向下封装的头部分,物数网传会表应不多介绍了哈,了解下OSI7层模型):官方AJP协议介绍文档
CVE-2020-1938漏洞分析
/conf/server.xml中可配置connector,这个也是官方临时修复建议需要注释掉的点也就是关闭8009端口,关闭了AJP协议,自然就没有此漏洞了,此漏洞需要借助ajp协议进行攻击。
CVE-2020-1938漏洞分析

漏洞成因

tomcat7.0.99举例:下载位置

首先看二进制的输入点,定位到ajp中调用socket的点,位置为/org/apache/coyote/ajp目录,索引socket关键字。

先看下安恒给的点AjpProcessor.java
CVE-2020-1938漏洞分析
CVE-2020-1938漏洞分析
预请求的prepareRequest()方法来自于父类AbstractAjpProcessor,找下
CVE-2020-1938漏洞分析

关键点在如下代码中,在这个之前呢,贴几个图,当然可以下一个tomcat版本自己看,各版本有细微差异,比如枚举的值有的直接就是赋值硬编码,可能是后来改进了。
CVE-2020-1938漏洞分析
CVE-2020-1938漏洞分析
CVE-2020-1938漏洞分析
CVE-2020-1938漏洞分析
下面的代码加上详细注释方便新手理解,因为一般相对了解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的其实不重要,很多人可能会苦读这块内容导致被绕晕。
CVE-2020-1938漏洞分析
接着就到了HttpServlet处理请求的点

CVE-2020-1938漏洞分析
CVE-2020-1938漏洞分析
CVE-2020-1938漏洞分析
CVE-2020-1938漏洞分析
在defaultservlet中包含获取的属性
CVE-2020-1938漏洞分析CVE-2020-1938漏洞分析
CVE-2020-1938漏洞分析
到这里基本漏洞的原理就差不多了,最后就是注意一下file中会对path进行限制跨目录
CVE-2020-1938漏洞分析
至于jsp文件的处理基本也差不多,核心漏洞输入点在上文ajp-http中,输出处理不一样罢了。

复现方式可以利用脚本,也可以利用java客户端发送ajp数据包。

上一篇:常用的ELK/EFK架构


下一篇:es 安全方面