/
kerberos_enumusers.rb
124 lines (109 loc) · 3.47 KB
/
kerberos_enumusers.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
class MetasploitModule < Msf::Auxiliary
include Msf::Auxiliary::Report
include Msf::Exploit::Remote::Kerberos::Client
def initialize(info = {})
super(update_info(info,
'Name' => 'Kerberos Domain User Enumeration',
'Description' => %q(
This module will enumerate valid Domain Users via Kerberos from an unauthenticated perspective. It utilizes
the different responses returned by the service for valid and invalid users.
),
'Author' =>
[
'Matt Byrne <attackdebris[at]gmail.com>' # Metasploit module
],
'References' =>
[
[ 'URL', 'https://nmap.org/nsedoc/scripts/krb5-enum-users.html']
],
'License' => MSF_LICENSE
)
)
register_options(
[
OptString.new('DOMAIN', [ true, 'The Domain Eg: demo.local' ]),
OptPath.new(
'USER_FILE',
[true, 'Files containing usernames, one per line', nil]
)
],
self.class
)
end
def user_list
users = nil
if File.readable? datastore['USER_FILE']
users = File.new(datastore['USER_FILE']).read.split
users.each { |u| u.downcase! }
users.uniq!
else
raise ArgumentError, "Cannot read file #{datastore['USER_FILE']}"
end
users
end
def run
print_status("Validating options...")
domain = datastore['DOMAIN'].upcase
user_file = datastore['USER_FILE']
print_status("Using domain: #{domain}...")
pre_auth = []
pre_auth << build_pa_pac_request
pre_auth
user_list.each do |user|
print_status("#{peer} - Testing User: \"#{user}\"...")
res = send_request_as(
client_name: "#{user}",
server_name: "krbtgt/#{domain}",
realm: "#{domain}",
pa_data: pre_auth
)
print_status("#{peer} - #{warn_error(res)}") if res.msg_type == Rex::Proto::Kerberos::Model::KRB_ERROR
test = Rex::Proto::Kerberos::Model::ERROR_CODES[res.error_code]
if test == ["KDC_ERR_PREAUTH_REQUIRED", "Additional pre-authentication required"]
print_good("#{peer} - User: \"#{user}\" is present")
report_cred(
host: datastore['RHOST'],
port: rport,
creds_name: 'Kerberos',
user: user
)
elsif test == ["KDC_ERR_CLIENT_REVOKED", "Clients credentials have been revoked"]
print_error("#{peer} - User: \"#{user}\" account disabled or locked out")
else
print_status("#{peer} - User: \"#{user}\" does not exist")
end
end
end
def report_cred(opts)
service_data = {
address: opts[:host],
port: opts[:port],
protocol: 'udp',
workspace_id: myworkspace_id,
service_name: opts[:creds_name]
}
credential_data = {
username: opts[:user],
origin_type: :service,
module_fullname: self.fullname
}.merge(service_data)
login_data = {
core: create_credential(credential_data),
status: Metasploit::Model::Login::Status::UNTRIED
}.merge(service_data)
create_credential_login(login_data)
end
def warn_error(res)
msg = ''
if Rex::Proto::Kerberos::Model::ERROR_CODES.key?(res.error_code)
error_info = Rex::Proto::Kerberos::Model::ERROR_CODES[res.error_code]
msg = "#{error_info[0]} - #{error_info[1]}"
else
msg = 'Wrong DOMAIN Name? Check DOMAIN and retry...'
end
end
end