Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Mssql session type #18747

Merged
merged 1 commit into from Feb 9, 2024
Merged

Conversation

zgoldman-r7
Copy link
Contributor

@zgoldman-r7 zgoldman-r7 commented Jan 25, 2024

This PR takes the existing MSSQL Client work and adds the session type on top of it.

Verification

List the steps needed to make sure this thing works

  • ./msfconsole -q
  • features set mssql_session_type true
  • save
  • exit
  • ./msfconsole -q
  • spin up an MSSQL instance
  • use mssql_login
  • set all options appropriately, including set CreateSession true
  • run
  • verify that you have a session with sessions -i -1
  • read the help logs and try things out
  • run SQL queries with the query 'YOUR_QUERY_HERE' and verify results

Example interaction:

msf6 auxiliary(scanner/mssql/mssql_login) > run

[*] 192.168.2.208:1433 - MSSQL - Starting authentication scanner.
[+] 192.168.2.208:1433 - Login Successful: WORKSTATION\test:ASDqwe123
[*] MSSQL session 1 opened (192.168.2.1:54471 -> 192.168.2.208:1433) at 2024-01-25 16:30:42 -0600
[*] Scanned 1 of 1 hosts (100% complete)
[*] Auxiliary module execution completed
msf6 auxiliary(scanner/mssql/mssql_login) > sessions -i -1
[*] Starting interaction with 1...

MSSQL @ 192.168.2.208:1433 (WORKSTATION) > query 'select @@version;'
[*] Sending statement: 'select @@version;'...

[*] SQL Query: select @@version;
[*] Row Count: 1 (Status: 16 Command: 193)



 NULL
 ----
 Microsoft SQL Server 2022 (RTM) - 16.0.1000.6 (X64)
	Oct  8 2022 05:58:25
	Copyright (C) 2022 Microsoft Corporation
	Developer Editio
 n (64-bit) on Windows Server 2022 Standard 10.0 <X64> (Build 20348: ) (Hypervisor)

subject(:command_dispatcher) { described_class.new(session.console) }

it_behaves_like 'session command dispatcher'
end
Copy link
Contributor

Choose a reason for hiding this comment

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

Looks like you'll want to set up your text editor to add a new line on save, and I imagine you're missing the option to strip superfluous whitespace too 👀

Comment on lines 105 to 113
print_line 'Usage: query'
print_line
print_line 'Run a raw SQL query on the target.'
print_line 'Examples:'
print_line
print_line ' query SHOW DATABASES;'
print_line ' query USE information_schema;'
print_line ' query SELECT * FROM SQL_FUNCTIONS;'
print_line ' query SELECT version();'
print_line
Copy link
Contributor

Choose a reason for hiding this comment

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

This is Postgres stuff, should replace with MSSQL commands that do +- same things from this list

Comment on lines 78 to 94
finished = false
until finished
begin
raw_query = ::Reline.readmultiline('SQL >> ', use_history = true) do |multiline_input|
finished = stop_words.include?(multiline_input.split.last)
!multiline_input.split.last.end_with?('\\')
end
rescue ::Interrupt
finished = true
ensure
::Reline.prompt_proc = prompt_proc_before
end

if finished
print_status 'Exiting Shell mode.'
return
end
Copy link
Contributor

Choose a reason for hiding this comment

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

The way finished is handled here is a little redundant.

finished = false
until finished
begin
raw_query = ::Reline.readmultiline('SQL >> ', use_history = true) do |multiline_input|
Copy link
Contributor

Choose a reason for hiding this comment

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

We may want to look into using the existing history manager to store the SQL session commands.

@zgoldman-r7 zgoldman-r7 force-pushed the mssql-session-type branch 5 times, most recently from 0c65b9e to dc6dbfc Compare February 5, 2024 22:17
@zgoldman-r7 zgoldman-r7 marked this pull request as ready for review February 6, 2024 15:32
@@ -35,14 +36,30 @@ def initialize
OptBool.new('TDSENCRYPTION', [ true, 'Use TLS/SSL for TDS data "Force Encryption"', true]),
])

deregister_options('PASSWORD_SPRAY')
options_to_deregister = %w[PASSWORD_SPRAY]
unless framework.features.enabled?(Msf::FeatureManager::MSSQL_SESSION_TYPE)
Copy link
Contributor

Choose a reason for hiding this comment

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

We're adding in notification messages here now to let users know that the functionality exists, i.e. here's the postgres module code

    options_to_deregister = %w[SQL PASSWORD_SPRAY]
    if framework.features.enabled?(Msf::FeatureManager::POSTGRESQL_SESSION_TYPE)
      add_info('New in Metasploit 6.4 - The %grnCreateSession%clr option within this module can open an interactive session')
    else
      options_to_deregister << 'CreateSession'
    end
    deregister_options(*options_to_deregister)

Copy link
Contributor

Choose a reason for hiding this comment

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

Looks like this is still needing to be applied 👀

@sjanusz-r7
Copy link
Contributor

sjanusz-r7 commented Feb 7, 2024

Login Scanner

Wrong Port

msf6 auxiliary(scanner/mssql/mssql_login) > run rport=9001

[*] 192.168.112.3:9001    - 192.168.112.3:9001 - MSSQL - Starting authentication scanner.
[-] 192.168.112.3:9001    - 192.168.112.3:9001 - LOGIN FAILED: WORKSTATION\mssql-user:mssql-password1 (Unable to Connect: The connection with (192.168.112.3:9001) timed out.)
[-] 192.168.112.3:9001    - 192.168.112.3:9001 - LOGIN FAILED: WORKSTATION\mssql-user: (Unable to Connect: The connection with (192.168.112.3:9001) timed out.)
[*] 192.168.112.3:9001    - Scanned 1 of 1 hosts (100% complete)
[*] Auxiliary module execution completed

TDS Encryption On

[*] 192.168.112.3:1433    - 192.168.112.3:1433 - MSSQL - Starting authentication scanner.
[-] 192.168.112.3:1433    - Auxiliary failed: RuntimeError Cannot create sessions when encryption is enabled. See https://github.com/rapid7/metasploit-framework/issues/18745 to vote for this feature
[-] 192.168.112.3:1433    - Call stack:
[-] 192.168.112.3:1433    -   /Users/sjanusz/Programming/metasploit-framework/modules/auxiliary/scanner/mssql/mssql_login.rb:59:in `run_host'
[-] 192.168.112.3:1433    -   /Users/sjanusz/Programming/metasploit-framework/lib/msf/core/auxiliary/scanner.rb:128:in `block (2 levels) in run'
[-] 192.168.112.3:1433    -   /Users/sjanusz/Programming/metasploit-framework/lib/msf/core/thread_manager.rb:105:in `block in spawn'
[*] Auxiliary module execution completed

TDS Encryption Off & Wrong Username

This seems to be missing an error message? (Incorrect: )

msf6 auxiliary(scanner/mssql/mssql_login) > run rhost=192.168.112.3 rport=1433 stop_on_success=true username=mssql-user1 password=mssql-password1 tdsencryption=false

[*] 192.168.112.3:1433    - 192.168.112.3:1433 - MSSQL - Starting authentication scanner.
[-] 192.168.112.3:1433    - 192.168.112.3:1433 - LOGIN FAILED: WORKSTATION\mssql-user1:mssql-password1 (Incorrect: )
[-] 192.168.112.3:1433    - 192.168.112.3:1433 - LOGIN FAILED: WORKSTATION\mssql-user1: (Incorrect: )
[*] 192.168.112.3:1433    - Scanned 1 of 1 hosts (100% complete)
[*] Auxiliary module execution completed

TDS Encryption Off & Wrong Password

This seems to be missing an error message? (Incorrect: )

msf6 auxiliary(scanner/mssql/mssql_login) > run rhost=192.168.112.3 rport=1433 stop_on_success=true username=mssql-user password=wrong-password tdsencryption=false

[*] 192.168.112.3:1433    - 192.168.112.3:1433 - MSSQL - Starting authentication scanner.
[-] 192.168.112.3:1433    - 192.168.112.3:1433 - LOGIN FAILED: WORKSTATION\mssql-user:wrong-password (Incorrect: )
[-] 192.168.112.3:1433    - 192.168.112.3:1433 - LOGIN FAILED: WORKSTATION\mssql-user: (Incorrect: )
[*] 192.168.112.3:1433    - Scanned 1 of 1 hosts (100% complete)
[*] Auxiliary module execution completed

TDS Encryption Off & Correct Details

msf6 auxiliary(scanner/mssql/mssql_login) > run rhost=192.168.112.3 rport=1433 stop_on_success=true username=mssql-user password=mssql-password1 tdsencryption=false

[*] 192.168.112.3:1433    - 192.168.112.3:1433 - MSSQL - Starting authentication scanner.
[+] 192.168.112.3:1433    - 192.168.112.3:1433 - Login Successful: WORKSTATION\mssql-user:mssql-password1
[*] MSSQL session 2 opened (192.168.112.1:58465 -> 192.168.112.3:1433) at 2024-02-07 10:13:13 +0000
[*] 192.168.112.3:1433    - Scanned 1 of 1 hosts (100% complete)
[*] Auxiliary module execution completed

Domain name rather than IP:

This gets a session twice (this is an issue with RhostsWalker) 👍 .

msf6 auxiliary(scanner/mssql/mssql_login) > run rhost=dc1.sj.local rport=1433 stop_on_success=true username=mssql-user password=mssql-password1 tdsencryption=false

[*] 192.168.112.3:1433    - 192.168.112.3:1433 - MSSQL - Starting authentication scanner.
[+] 192.168.112.3:1433    - 192.168.112.3:1433 - Login Successful: WORKSTATION\mssql-user:mssql-password1
[*] MSSQL session 7 opened (192.168.112.1:58802 -> 192.168.112.3:1433) at 2024-02-07 10:45:11 +0000
[*] dc1.sj.local:1433     - Scanned 1 of 2 hosts (50% complete)
[*] 192.168.112.3:1433    - 192.168.112.3:1433 - MSSQL - Starting authentication scanner.
[+] 192.168.112.3:1433    - 192.168.112.3:1433 - Login Successful: WORKSTATION\mssql-user:mssql-password1
[*] MSSQL session 8 opened (192.168.112.1:58803 -> 192.168.112.3:1433) at 2024-02-07 10:45:12 +0000
[*] dc1.sj.local:1433     - Scanned 2 of 2 hosts (100% complete)
[*] Auxiliary module execution completed

URI:

msf6 auxiliary(scanner/mssql/mssql_login) > run mysql://mssql-user:mssql-password1@192.168.112.3:1433

[*] 192.168.112.3:1433    - 192.168.112.3:1433 - MSSQL - Starting authentication scanner.
[+] 192.168.112.3:1433    - 192.168.112.3:1433 - Login Successful: WORKSTATION\mssql-user:mssql-password1
[*] MSSQL session 6 opened (192.168.112.1:61069 -> 192.168.112.3:1433) at 2024-02-07 15:14:09 +0000
[*] mysql://mssql-user:mssql-password1@192.168.112.3:1433:1433 - Scanned 1 of 1 hosts (100% complete)
[*] Auxiliary module execution completed

Queries

When executing queries, there seems to be a few extra lines of blank space:

select @@version

MSSQL @ 192.168.112.3:1433 (WORKSTATION) > query select @@version
[*] Sending statement: 'select @@version'...

[*] 192.168.112.3:1433    - SQL Query: select @@version
[*] 192.168.112.3:1433    - Row Count: 1 (Status: 16 Command: 193)



 NULL
 ----
 Microsoft SQL Server 2022 (RTM) - 16.0.1000.6 (X64)
        Oct  8 2022 05:58:25
        Copyright (C) 2022 Microsoft Corporation
        Express Edition (64-bit) on Windows Server 2022 Standard 10.0 <X64>
 (Build 20348: ) (Hypervisor)

select user_name()

MSSQL @ 192.168.112.3:1433 (WORKSTATION) > query select user_name()
[*] Sending statement: 'select user_name()'...

[*] 192.168.112.3:1433    - SQL Query: select user_name()
[*] 192.168.112.3:1433    - Row Count: 1 (Status: 16 Command: 193)



 NULL
 ----
 guest

select name from master.dbo.sysdatabases

MSSQL @ 192.168.112.3:1433 (WORKSTATION) > query select name from master.dbo.sysdatabases
[*] Sending statement: 'select name from master.dbo.sysdatabases'...

[*] 192.168.112.3:1433    - SQL Query: select name from master.dbo.sysdatabases
[*] 192.168.112.3:1433    - Row Count: 4 (Status: 16 Command: 193)



 name
 ----
 master
 tempdb
 model
 msdb

Shell Mode

Fails when no input is provided and Enter key is pressed:

MSSQL @ 192.168.112.3:1433 (WORKSTATION) > shell
SQL >> {press enter here}[-] Error running command shell: NoMethodError undefined method `end_with?' for nil:NilClass

Inserting the query (with or without back-slashes) from https://learn.microsoft.com/en-us/sql/relational-databases/tables/create-tables-database-engine?view=sql-server-ver16#use-transact-sql: (copy paste)

CREATE TABLE dbo.PurchaseOrderDetail ( \
    PurchaseOrderID INT NOT NULL, \
    LineNumber SMALLINT NOT NULL, \
    ProductID INT NULL, \
    UnitPrice MONEY NULL, \
    OrderQty SMALLINT NULL, \
    ReceivedQty FLOAT NULL, \
    RejectedQty FLOAT NULL, \
    DueDate DATETIME NULL \
);

doesn't work - we can't input multi-line queries either with or without the \ character:

SQL >> select
[*] Running SQL Command: 'select'
[*] Sending statement: 'select'...
[*] 127.0.0.1:1433        - SQL Query: select
[-] 127.0.0.1:1433        - SQL Server Error #102 (State:1 Severity:15): Incorrect syntax near 'select'.
SQL >> select \
[*] Running SQL Command: 'select'
[*] Sending statement: 'select'...
[*] 127.0.0.1:1433        - SQL Query: select
[-] 127.0.0.1:1433        - SQL Server Error #102 (State:1 Severity:15): Incorrect syntax near 'select'.

Seems like the multi-line query code is inverse of what we want: (we can get multi-line input when ; char is entered)

MSSQL @ 127.0.0.1:1433 (WORKSTATION) > shell
SQL >> ;
SQL *> ;
SQL *> ;
SQL *> ;
SQL *> ;
SQL *>
SQL *>
SQL *>
SQL *>

Example query:

SQL >> select;
SQL >> @@version
[*] Running SQL Command: 'select; @@version'
[*] Sending statement: 'select; @@version'...
[*] 127.0.0.1:1433        - SQL Query: select; @@version
[-] 127.0.0.1:1433        - SQL Server Error #102 (State:1 Severity:15): Incorrect syntax near ';'.

We can run single-line queries in Shell mode though: (Note: The query doesn't end with ; due to the issue highlighted above)

CREATE TABLE dbo.PurchaseOrderDetail (PurchaseOrderID INT NOT NULL, LineNumber SMALLINT NOT NULL, ProductID INT NULL, UnitPrice MONEY NULL, OrderQty SMALLINT NULL, ReceivedQty FLOAT NULL, RejectedQty FLOAT NULL, DueDate DATETIME NULL)

The query seems to be output three times which seems unnecessary:

[*] Running SQL Command: 'CREATE TABLE dbo.PurchaseOrderDetail (PurchaseOrderID INT NOT NULL, LineNumber SMALLINT NOT NULL, ProductID INT NULL, UnitPrice MONEY NULL, OrderQty SMALLINT NULL, ReceivedQty FLOAT NULL, RejectedQty FLOAT NULL, DueDate DATETIME NULL)'
[*] Sending statement: 'CREATE TABLE dbo.PurchaseOrderDetail (PurchaseOrderID INT NOT NULL, LineNumber SMALLINT NOT NULL, ProductID INT NULL, UnitPrice MONEY NULL, OrderQty SMALLINT NULL, ReceivedQty FLOAT NULL, RejectedQty FLOAT NULL, DueDate DATETIME NULL)'...
[*] 127.0.0.1:1433        - SQL Query: CREATE TABLE dbo.PurchaseOrderDetail (PurchaseOrderID INT NOT NULL, LineNumber SMALLINT NOT NULL, ProductID INT NULL, UnitPrice MONEY NULL, OrderQty SMALLINT NULL, ReceivedQty FLOAT NULL, RejectedQty FLOAT NULL, DueDate DATETIME NULL)

Listing the table then shows:

SQL >> SELECT table_name FROM INFORMATION_SCHEMA.TABLES
[*] Running SQL Command: 'SELECT table_name FROM INFORMATION_SCHEMA.TABLES'
[*] Sending statement: 'SELECT table_name FROM INFORMATION_SCHEMA.TABLES'...
[*] 127.0.0.1:1433        - SQL Query: SELECT table_name FROM INFORMATION_SCHEMA.TABLES
[*] 127.0.0.1:1433        - Row Count: 7 (Status: 16 Command: 193)



 table_name
 ----------
 spt_fallback_db
 spt_fallback_dev
 spt_fallback_usg
 PurchaseOrderDetail
 spt_values
 spt_monitor
 MSreplication_options

MSSQL implements some functions we can call. Some of these are broken:
Running sp_help (https://learn.microsoft.com/en-us/previous-versions/sql/sql-server-2005/ms187335(v=sql.90)?redirectedfrom=MSDN):

SQL >> sp_help PurchaseOrderDetail
[*] Running SQL Command: 'sp_help PurchaseOrderDetail'
[*] Sending statement: 'sp_help PurchaseOrderDetail'...
[*] 127.0.0.1:1433        - SQL Query: sp_help PurchaseOrderDetail
[*] 127.0.0.1:1433        - Row Count: 8 (Status: 1 Command: 193)
[-] 127.0.0.1:1433        - unsupported token: 0
[-] 127.0.0.1:1433        - unsupported token: 112
[-] 127.0.0.1:1433        - unsupported token: 0
[-] Error running command shell: Rex::RuntimeError Invalid number of columns!
  • Another broken tested helper is sp_monitor.

Running sp_who (https://learn.microsoft.com/en-us/previous-versions/sql/sql-server-2005/ms174313(v=sql.90)) works:

MSSQL @ 127.0.0.1:1433 (WORKSTATION) > shell
SQL >> sp_who
[*] Running SQL Command: 'sp_who'
[*] Sending statement: 'sp_who'...

[*] 127.0.0.1:1433        - SQL Query: sp_who



 spid  ecid  status      loginame             hostname      blk  dbname  cmd                       request_id
 ----  ----  ------      --------             --------      ---  ------  ---                       ----------
 1     0     sleeping    sa                                 0    master  TASK MANAGER              0
 2     0     sleeping    sa                                 0    master  TASK MANAGER              0
 3     0     sleeping    sa                                 0    master  TASK MANAGER              0
 4     0     sleeping    sa                                 0    master  TASK MANAGER              0
 5     0     sleeping    sa                                 0    master  TASK MANAGER              0
 6     0     sleeping    sa                                 0    master  TASK MANAGER              0
 7     0     sleeping    sa                                 0    master  TASK MANAGER              0
 8     0     sleeping    sa                                 0    master  TASK MANAGER              0
 9     0     sleeping    sa                                 0    master  TASK MANAGER              0
 10    0     sleeping    sa                                 0    master  TASK MANAGER              0
 12    0     sleeping    sa                                 0    master  TASK MANAGER              0
 13    0     sleeping    sa                                 0    master  TASK MANAGER              0

Backgrounding

Currently broken:

MSSQL @ 192.168.112.3:1433 (WORKSTATION) > shell
SQL >> {ctrl + z here}
Background session 13? [y/N]  [-] Error running command shell: NoMethodError undefined method `end_with?' for nil:NilClass

This should be fixed if we implement the CTRL + Z/C changes from Postgres, but this will be also fixed as part of my future SQL client consolidation PR.

Session Info & Prompt - Default Database

This is currently not implemented correctly, I believe.
In MSSQL, I set the default database for my user to master. This is proven by:

  • Selecting the currently connected database:
MSSQL @ 192.168.112.3:1433 (WORKSTATION) > query 'SELECT DB_NAME()'
[*] Sending statement: 'SELECT DB_NAME()'...

[*] 192.168.112.3:1433    - SQL Query: SELECT DB_NAME()
[*] 192.168.112.3:1433    - Row Count: 1 (Status: 16 Command: 193)



 NULL
 ----
 master

However in the session prompt the cwd (representing our current DB) we get:

MSSQL @ 192.168.112.3:1433 (WORKSTATION)

This should be:

MSSQL @ 192.168.112.3:1433 (master)

After enabling a different user login (sa for example) and setting the default database to msdb:

MSSQL @ 192.168.112.3:1433 (WORKSTATION) > query 'SELECT DB_NAME()'
[*] Sending statement: 'SELECT DB_NAME()'...

[*] 192.168.112.3:1433    - SQL Query: SELECT DB_NAME()
[*] 192.168.112.3:1433    - Row Count: 1 (Status: 16 Command: 193)



 NULL
 ----
 msdb

The prompt still said WORKSTATION but our current DB was msdb.

run command

This works:

msf6 auxiliary(scanner/postgres/postgres_login) > sessions

Active sessions
===============

  Id  Name  Type   Information                            Connection
  --  ----  ----   -----------                            ----------
  1         MSSQL  MSSQL mssql-user @ 192.168.112.3:1433  192.168.112.1:60755 -> 192.168.112.3:1433 (192.168.112.3)
  2         MSSQL  MSSQL mssql-user @ 192.168.112.3:1433  192.168.112.1:61055 -> 192.168.112.3:1433 (192.168.112.3)
  3         MSSQL  MSSQL mssql-user @ 192.168.112.3:1433  192.168.112.1:61059 -> 192.168.112.3:1433 (192.168.112.3)
  4         MSSQL  MSSQL mssql-user @ 192.168.112.3:1433  192.168.112.1:61061 -> 192.168.112.3:1433 (192.168.112.3)

msf6 auxiliary(scanner/postgres/postgres_login) > cat autoruns.rc
[*] exec: cat autoruns.rc

<ruby>
$stderr.puts "hello world from mssql"
$stderr.puts self
run_single("query SELECT @@version;")
</ruby>
msf6 auxiliary(scanner/postgres/postgres_login) > sessions -i -1
[*] Starting interaction with 4...

MSSQL @ 192.168.112.3:1433 (WORKSTATION) > run autoruns.rc
[*] Processing autoruns.rc for ERB directives.
[*] resource (autoruns.rc)> Ruby Code (94 bytes)
hello world from mssql
#<Rex::Post::MSSQL::Ui::Console:0x00007fe29c907aa0>
[*] Sending statement: 'SELECT @@version;'...

[*] 192.168.112.3:1433    - SQL Query: SELECT @@version;
[*] 192.168.112.3:1433    - Row Count: 1 (Status: 16 Command: 193)



 NULL
 ----
 Microsoft SQL Server 2022 (RTM) - 16.0.1000.6 (X64)
        Oct  8 2022 05:58:25
        Copyright (C) 2022 Microsoft Corporation
        Express Edition (64-bit) on Windows Server 2022 Standard 10.0 <X64>
 (Build 20348: ) (Hypervisor)

Autoruns

This functionality is currently broken:

msf6 auxiliary(scanner/mssql/mssql_login) > run autorunscript=./autorun.rc

[*] 192.168.112.3:1433    - 192.168.112.3:1433 - MSSQL - Starting authentication scanner.
[+] 192.168.112.3:1433    - 192.168.112.3:1433 - Login Successful: WORKSTATION\mssql-user:mssql-password1
[-] 192.168.112.3:1433    - 192.168.112.3:1433 - LOGIN FAILED: WORKSTATION\mssql-user: (Incorrect: )
[*] 192.168.112.3:1433    - Scanned 1 of 1 hosts (100% complete)
[*] Auxiliary module execution completed
msf6 auxiliary(scanner/mssql/mssql_login) > run autorunscript=autorun.rc

[*] 192.168.112.3:1433    - 192.168.112.3:1433 - MSSQL - Starting authentication scanner.
[+] 192.168.112.3:1433    - 192.168.112.3:1433 - Login Successful: WORKSTATION\mssql-user:mssql-password1
[-] 192.168.112.3:1433    - 192.168.112.3:1433 - LOGIN FAILED: WORKSTATION\mssql-user: (Incorrect: )
[*] 192.168.112.3:1433    - Scanned 1 of 1 hosts (100% complete)
[*] Auxiliary module execution completed
msf6 auxiliary(scanner/mssql/mssql_login) > run autorunscript='autorun.rc'

[*] 192.168.112.3:1433    - 192.168.112.3:1433 - MSSQL - Starting authentication scanner.
[+] 192.168.112.3:1433    - 192.168.112.3:1433 - Login Successful: WORKSTATION\mssql-user:mssql-password1
[-] 192.168.112.3:1433    - 192.168.112.3:1433 - LOGIN FAILED: WORKSTATION\mssql-user: (Incorrect: )
[*] 192.168.112.3:1433    - Scanned 1 of 1 hosts (100% complete)
[*] Auxiliary module execution completed
msf6 auxiliary(scanner/mssql/mssql_login) > run autorunscript='autorun.rc' verbose=true

[*] 192.168.112.3:1433    - 192.168.112.3:1433 - MSSQL - Starting authentication scanner.
[+] 192.168.112.3:1433    - 192.168.112.3:1433 - Login Successful: WORKSTATION\mssql-user:mssql-password1
[-] 192.168.112.3:1433    - 192.168.112.3:1433 - LOGIN FAILED: WORKSTATION\mssql-user: (Incorrect: )
[*] 192.168.112.3:1433    - Scanned 1 of 1 hosts (100% complete)
[*] Auxiliary module execution complete

Sessions are being open though and work when interacted with:

msf6 auxiliary(scanner/mssql/mssql_login) > sessions

Active sessions
===============

  Id  Name  Type   Information                            Connection
  --  ----  ----   -----------                            ----------
  1         MSSQL  MSSQL mssql-user @ 192.168.112.3:1433  192.168.112.1:60755 -> 192.168.112.3:1433 (192.168.112.3)
  2         MSSQL  MSSQL mssql-user @ 192.168.112.3:1433  192.168.112.1:61055 -> 192.168.112.3:1433 (192.168.112.3)
  3         MSSQL  MSSQL mssql-user @ 192.168.112.3:1433  192.168.112.1:61059 -> 192.168.112.3:1433 (192.168.112.3)
  4         MSSQL  MSSQL mssql-user @ 192.168.112.3:1433  192.168.112.1:61061 -> 192.168.112.3:1433 (192.168.112.3)
  5         MSSQL  MSSQL mssql-user @ 192.168.112.3:1433  192.168.112.1:61063 -> 192.168.112.3:1433 (192.168.112.3)

Wiring up sessions -c functionality

This functionality currently is not implemented, but it isn't implemented in Postgres session type right now either (could be fixed in the consolidation PR in the future).
We could potentially wire up something similar to the below git diff/patch (not optimal, take it with a grain of salt):

diff --git a/lib/msf/base/sessions/mssql.rb b/lib/msf/base/sessions/mssql.rb
index 41b756499b..a5e8bde0a0 100644
--- a/lib/msf/base/sessions/mssql.rb
+++ b/lib/msf/base/sessions/mssql.rb
@@ -149,4 +149,13 @@ class Msf::Sessions::MSSQL
     raise EOFError if (console.stopped? == true)
   end

+  def run_cmd(cmd)
+    if cmd.split.first&.downcase == 'query'
+      # Drop the `query` and run the remaining cmd
+      # The remaining cmd has an extra set of speech marks (") on each side, remove them.
+      modified_cmd = cmd.split[1..].join(' ').delete_prefix('"').delete_suffix('"')
+      client.mssql_query(modified_cmd, true)
+    end
+  end
+
 end
diff --git a/lib/msf/ui/console/command_dispatcher/core.rb b/lib/msf/ui/console/command_dispatcher/core.rb
index d91399346c..f412605346 100644
--- a/lib/msf/ui/console/command_dispatcher/core.rb
+++ b/lib/msf/ui/console/command_dispatcher/core.rb
@@ -1625,6 +1625,8 @@ class Core
             elsif session.type == 'shell' || session.type == 'powershell'
               output = session.shell_command(cmd)
               print_line(output) if output
+            elsif session.type == 'MSSQL' || session.type == 'PostgreSQL' || session.type == 'MySQL'
+              session.run_cmd(cmd)
             end
           ensure
             # Restore timeout for each session

This would let us call:

msf6 auxiliary(scanner/mssql/mssql_login) > sessions -i -1 -c 'query "xp_msver"'
[*] Running 'query "xp_msver"' on MSSQL session -1 (192.168.112.3)

[*] 192.168.112.3:1433    - SQL Query: xp_msver



 Index  Name                 Internal_Value  Character_Value
 -----  ----                 --------------  ---------------
 1      ProductName                          Microsoft SQL Server
 2      ProductVersion       1048576         16.0.1000.6
 3      Language             1033            English (United States)
 4      Platform                             NT x64
 5      Comments                             SQL
 6      CompanyName                          Microsoft Corporation
 7      FileDescription                      SQL Server Windows NT - 64 Bit
 8      FileVersion                          2022.0160.1000.06 ((SQL22_RTM).221008-0913)
 9      InternalName                         SQLSERVR
 10     LegalCopyright                       Microsoft. All rights reserved.
 11     LegalTrademarks                      Microsoft SQL Server is a registered trademark of Microsoft Corporation.
 12     OriginalFilename                     SQLSERVR.EXE
 13     PrivateBuild
 14     SpecialBuild         65536006
 15     WindowsVersion       65536006        6.3 (20348)
 16     ProcessorCount       4               4
 17     ProcessorActiveMask                  f
 18     ProcessorType        8664
 19     PhysicalMemory       4095            4095 (4293959680)
 20     Product ID

Thoughts on this functionality?

Let me know if any of the steps above aren't as expected or I should re-test anything 👍

payload.exe Outdated Show resolved Hide resolved
@@ -47,6 +47,8 @@ class MSSQL
# @return [Boolean] Whether to use Windows Authentication instead of SQL Server Auth.
attr_accessor :windows_authentication

attr_accessor :use_client_as_proof
Copy link
Contributor

Choose a reason for hiding this comment

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

Can we make sure we've added all of the yard doc types here - and other places:

# @returns [Boolean] If a login is successful and this attribute is true - a PostgreSQL::Client instance is used as proof,
# and the socket is not immediately closed
attr_accessor :use_client_as_proof

@zgoldman-r7 zgoldman-r7 force-pushed the mssql-session-type branch 2 times, most recently from c37a7ca to fce9420 Compare February 8, 2024 14:39
@adfoster-r7
Copy link
Contributor

The commit history is a bit strange - it looks like the first commit has merge conflicts present. Might be good to squash into the one commit and ensure you're rebased against the latest master code - let me know if I've misunderstood anything though 👍

@zgoldman-r7 zgoldman-r7 force-pushed the mssql-session-type branch 3 times, most recently from 7181765 to 865220c Compare February 8, 2024 18:41
def session_setup(result, client)
return unless (result && client)
rstream = client.sock
my_session = Msf::Sessions::MSSQL.new(rstream, { client: client }) # is cwd right?
Copy link
Contributor

Choose a reason for hiding this comment

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

Simon will align the other clients not to use cwd as a convention, but database_name or something else 😄

Comment on lines 53 to 59
def rhost
self.address
end

def rport
self.port
end
Copy link
Contributor

Choose a reason for hiding this comment

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

Why did we need to redefine this here? 👀

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This was per a conversation with @dwelch-r7 where we talked about ripping the rhost/rport accessors from lib/msf/core/post/common.rb and putting them directly in the session.

Tagging to correct me if I misunderstood the context

Copy link
Contributor

Choose a reason for hiding this comment

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

yea I think something got lost in translation somewhere along the line, it seems like these aren't being referenced anywhere either?

Copy link
Contributor

Choose a reason for hiding this comment

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

Looks like it's good to remove this then 🙌

@@ -114,7 +114,7 @@ def report_hashes(mssql_hashes, version_year)

service_data = {
address: ::Rex::Socket.getaddress(rhost,true),
port: rport,
port: datastore['RPORT'],
Copy link
Contributor

Choose a reason for hiding this comment

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

I think this has snuck in here accidentally, it looks like a rebase issue maybe?

Reminds me of this thread: #18696 (comment)

[
Msf::OptInt.new('SESSION', [ false, 'The session to run this module on' ]),
Msf::OptString.new('DATABASE', [ false, 'The database to authenticate against', 'MSSQL']),
Msf::OptString.new('USERNAME', [ false, 'The username to authenticate as', 'MSSQL']),
Msf::Opt::RHOST(nil, false),
Msf::Opt::RPORT(nil, false)
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
Msf::Opt::RPORT(nil, false)
Msf::Opt::RPORT(1433, false)

# The mssql client context
self.session = session
self.client = session.client
self.cwd = session.client.mssql_query('SELECT DB_NAME();')[:rows][0][0]
Copy link
Contributor

Choose a reason for hiding this comment

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

Not a blocker; Looks like this information is already given to us during the login negotiation process via wireshark:

image

And reading the spec:

The type of environment change:

1: Database
https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-tds/2b3eb7e5-d43d-4d1b-bf4d-76b9e3afc791

So to do this without an extra query, all we'd need to do is something similar to using the info that we parse here:

From: /Users/user/Documents/code/metasploit-framework/lib/rex/proto/mssql/client.rb:414 Rex::Proto::MSSQL::Client#mssql_login:

    409: 
    410:           info = {:errors => []}
    411:           info = mssql_parse_reply(resp, info)
    412: 
    413:           require 'pry-byebug'; binding.pry
 => 414:           return false if not info
    415:           info[:login_ack] ? true : false
    416:         end
    417: 
    418:         #
    419:         #this method send a prelogin packet and check if encryption is off

[1] pry(#<Rex::Proto::MSSQL::Client>)> info
=> {:errors=>[],
 :envs=>
  [{:type=>1, :old=>"master", :new=>"master"},
   {:type=>7, :old=>"", :new=>"\t\x04\xD04"},
   {:type=>2, :old=>"", :new=>"us_english"},
   {:type=>4, :old=>"4096", :new=>"4096"}],
 :infos=>
  ["SQL Server Info #5701 (State:2 Severity:0): Changed database context to 'master'.",
   "SQL Server Info #5703 (State:1 Severity:0): Changed language setting to us_english."],
 :login_ack=>true,
 :done=>{:status=>0, :cmd=>0, :rows=>0}}
[2] pry(#<Rex::Proto::MSSQL::Client>)> 

And pull out the database type using the type=>1 reference from the docs:

  [{:type=>1, :old=>"master", :new=>"master"},

And expose it on the mssql client instance for future users

print_line 'Usage: shell'
print_line
print_line 'Go into a raw SQL shell where SQL queries can be executed.'
print_line 'To exit, type `exit`, `quit`, `end` or `stop`.'
Copy link
Contributor

Choose a reason for hiding this comment

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

Not a blocker since it's from the other pull requests; but we generally don't use back ticks in prompt outputs

Comment on lines 143 to 145

alias cmd_sql cmd_query
alias cmd_sql_help cmd_query_help
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
alias cmd_sql cmd_query
alias cmd_sql_help cmd_query_help

@@ -27,6 +27,7 @@ class Client
attr_accessor :ssl_cipher
attr_accessor :proxies
attr_accessor :connection_timeout
attr_accessor :realm
Copy link
Contributor

Choose a reason for hiding this comment

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

Any context on this change? 👀

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Forgot to rip it out, was using this (incorrectly) for cwd

@@ -58,7 +58,7 @@ def mssql_print_reply(info)
tbl << row
end

print_line(tbl.to_s)
print_line(tbl.to_s.gsub("\n\n\n","\n"))
Copy link
Contributor

Choose a reason for hiding this comment

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

This looks like a hack; It looks like the extra whitespace is caused by the Header being blank

      tbl = Rex::Text::Table.new(
        'Indent'    => 1,
-        'Header'    => "",
+        'Header'    => "Response",
        'Columns'   => info[:colnames],
        'SortIndex' => -1
      )

Which would give a cleaner output without the whitespace that I think you were trying to remove

[*] 192.168.123.136:1433  - SQL Query: select @@version;
[*] 192.168.123.136:1433  - Row Count: 1 (Status: 16 Command: 193)
Response
========

 NULL
 ----
 Microsoft SQL Server 2022 (RTM) - 16.0.1000.6 (X64) 
	Oct  8 2022 05:58:25 
	Copyright (C) 2022 Microsoft Corporation
	Express Edition (64-bit) on Windows Server 2022 Standard Evaluation 10.0 <X64> (Build 20348: ) (Hypervisor)

@@ -24,6 +24,7 @@ def initialize(info = {})
def run
# Check connection and issue initial query
print_status("Attempting to connect to the database server at #{rhost}:#{rport} as #{datastore['USERNAME']}...")

Copy link
Contributor

Choose a reason for hiding this comment

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

I'm guessing this left over from a rebase; we should be good to revert this change 👍

@@ -24,6 +24,7 @@ def initialize(info = {})
def run
# Check connection and issue initial query
print_status("Attempting to connect to the database server at #{rhost}:#{rport} as #{datastore['USERNAME']}...")

Copy link
Contributor

Choose a reason for hiding this comment

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

I'm guessing this left over from a rebase; we should be good to revert this change 👍

if create_session?
raise "Cannot create sessions when encryption is enabled. See https://github.com/rapid7/metasploit-framework/issues/18745 to vote for this feature"
else
print_status("Manually enabled TLS/SSL to encrypt TDS payloads.")
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
print_status("Manually enabled TLS/SSL to encrypt TDS payloads.")
print_status("TDS Encryption enabled")

end

def run_host(ip)
print_status("#{rhost}:#{rport} - MSSQL - Starting authentication scanner.")

if datastore['TDSENCRYPTION']
print_status("Manually enabled TLS/SSL to encrypt TDS payloads.")
if create_session?
raise "Cannot create sessions when encryption is enabled. See https://github.com/rapid7/metasploit-framework/issues/18745 to vote for this feature"
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
raise "Cannot create sessions when encryption is enabled. See https://github.com/rapid7/metasploit-framework/issues/18745 to vote for this feature"
raise Msf::OptionValidateError.new(
{
'TDSENCRYPTION' => "Cannot create sessions when encryption is enabled. See https://github.com/rapid7/metasploit-framework/issues/18745 to vote for this feature"
}
)

Before

Cryptic stacktrace:


msf6 auxiliary(scanner/mssql/mssql_login) > run 192.168.123.136  username=admin password=p4$$w0rd  tdsencryption=true createsession=true

[*] 192.168.123.136:1433  - 192.168.123.136:1433 - MSSQL - Starting authentication scanner.
[-] 192.168.123.136:1433  - Auxiliary failed: RuntimeError Cannot create sessions when encryption is enabled. See https://github.com/rapid7/metasploit-framework/issues/18745 to vote for this feature
[-] 192.168.123.136:1433  - Call stack:
[-] 192.168.123.136:1433  -   /Users/user/Documents/code/metasploit-framework/modules/auxiliary/scanner/mssql/mssql_login.rb:61:in `run_host'
[-] 192.168.123.136:1433  -   /Users/user/Documents/code/metasploit-framework/lib/msf/core/auxiliary/scanner.rb:128:in `block (2 levels) in run'
[-] 192.168.123.136:1433  -   /Users/user/Documents/code/metasploit-framework/lib/msf/core/thread_manager.rb:105:in `block in spawn'
[*] Auxiliary module execution completed

After

Cleaner UI that aligns with other module validation

msf6 auxiliary(scanner/mssql/mssql_login) > reload
[*] Reloading module...
[*] New in Metasploit 6.4 - The CreateSession option within this module can open an interactive session
msf6 auxiliary(scanner/mssql/mssql_login) > run 192.168.123.136  username=admin password=p4$$w0rd  tdsencryption=true createsession=true

[*] 192.168.123.136:1433  - 192.168.123.136:1433 - MSSQL - Starting authentication scanner.
[-] 192.168.123.136:1433  - Msf::OptionValidateError The following options failed to validate:
[-] 192.168.123.136:1433  - Invalid option TDSENCRYPTION: Cannot create sessions when encryption is enabled. See https://github.com/rapid7/metasploit-framework/issues/18745 to vote for this feature
[*] Auxiliary module execution completed
msf6 auxiliary(scanner/mssql/mssql_login) > 

if use_client_as_proof
result_options[:proof] = client
else
client.disconnect # replacing the ensure so the client doesn't disconnect on login - is this right?
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
client.disconnect # replacing the ensure so the client doesn't disconnect on login - is this right?
client.disconnect

Looks good 👍

@zgoldman-r7 zgoldman-r7 force-pushed the mssql-session-type branch 2 times, most recently from f808d04 to 4756e65 Compare February 9, 2024 13:05
@adfoster-r7 adfoster-r7 merged commit 9caa2fa into rapid7:master Feb 9, 2024
49 checks passed
@adfoster-r7
Copy link
Contributor

Release Notes

Updates the auxiliary/scanner/mssql/mssql_login module with a new CreateSession option which controls the opening of an interactive MSSQL session. This functionality is currently behind a feature flag which can be enabled with features set mssql_session_type true

@adfoster-r7 adfoster-r7 added the rn-enhancement release notes enhancement label Feb 9, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
rn-enhancement release notes enhancement
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

6 participants