#!/usr/bin/env ruby # # $Id$ # $Revision$ # msfbase = __FILE__ while File.symlink?(msfbase) msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase)) end $:.unshift(File.expand_path(File.join(File.dirname(msfbase), 'lib'))) require 'fastlib' $:.unshift(ENV['MSF_LOCAL_LIB']) if ENV['MSF_LOCAL_LIB'] require 'rex' require 'msf/ui' 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" ], "-v" => [ false, "Increase verbosity" ], # 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" ], # 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" ], "-c" => [ true, "The number of times to encode the data" ], # 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 # def dump_encoders(arch = nil) tbl = Rex::Ui::Text::Table.new( 'Indent' => 4, 'Header' => "Framework Encoders" + ((arch) ? " (architectures: #{arch})" : ""), 'Columns' => [ "Name", "Rank", "Description" ]) cnt = 0 $framework.encoders.each_module( 'Arch' => arch ? arch.split(',') : nil) { |name, mod| tbl << [ name, mod.rank_to_s, mod.new.name ] cnt += 1 } (cnt > 0) ? "\n" + tbl.to_s + "\n" : "\nNo compatible encoders found.\n\n" end # # Returns the list of encoders to try # def get_encoders(arch, encoder) encoders = [] if (encoder) encoders << $framework.encoders.create(encoder) else $framework.encoders.each_module_ranked( 'Arch' => arch ? arch.split(',') : nil) { |name, mod| encoders << mod.new } end encoders end # # Nuff said. # def usage $stderr.puts("\n" + " Usage: #{$0} \n" + $args.usage) exit end def write_encoded(buf) if (not $output) $stdout.write(buf) else File.open($output, "wb") do |fd| fd.write(buf) end end end # Defaults verbose = 0 cmd = "encode" arch = nil badchars = '' space = nil encoder = nil fmt = nil input = $stdin options = '' delim = '_|_' output = nil ecount = 1 plat = nil altexe = nil inject = false exedir = nil # use default # Parse the argument and rock that shit. $args.parse(ARGV) { |opt, idx, val| case opt when "-i" begin input = File.open(val, 'rb') rescue $stderr.puts(OutError + "Failed to open file #{val}: #{$!}") exit end when "-m" $framework.modules.add_module_path(val) when "-l" cmd = "list" when "-n" cmd = "dump" when "-a" arch = val when "-c" ecount = val.to_i when "-b" badchars = Rex::Text.hex_to_raw(val) when "-p" plat = Msf::Module::PlatformList.transform(val) when "-s" space = val.to_i when "-t" if supported_formats.include?(val) fmt = val else $stderr.puts(OutError + "Invalid format: #{val}") exit end when "-o" $output = val when "-e" encoder = val when "-d" exedir = val when "-x" altexe = val when "-k" inject = true when "-h" usage when "-v" verbose += 1 else if (val =~ /=/) options += ((options.length > 0) ? delim : "") + "#{val}" end end } if(not fmt and output) pre,ext = output.split('.') if(ext and not ext.empty?) fmt = ext end end 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( :module_types => [ Msf::MODULE_ENCODER, Msf::MODULE_NOP ], 'DisableDatabase' => true ) # Get the list of encoders to try encoders = get_encoders(arch, encoder) # Process the actual command case cmd when "list" $stderr.puts(dump_encoders(arch)) when "dump" enc = encoder ? $framework.encoders.create(encoder) : nil if (enc) $stderr.puts(Msf::Serializer::ReadableText.dump_module(enc)) else $stderr.puts(OutError + "Invalid encoder specified.") end when "encode" input.binmode # ensure its in binary mode buf = input.read encoders.each { |enc| next if not enc begin # Imports options enc.datastore.import_options_from_s(options, delim) skip = false eout = buf.dup raw = nil 1.upto(ecount) do |iteration| # Encode it up raw = enc.encode(eout, badchars, nil, plat) # Is it too big? if (space and space > 0 and raw.length > space) $stderr.puts(OutError + "#{enc.refname} created buffer that is too big (#{raw.length})") skip = true break end # Print it out $stderr.puts(OutStatus + "#{enc.refname} succeeded with size #{raw.length} (iteration=#{iteration})\n\n") eout = raw end next if skip output = Msf::Util::EXE.to_executable_fmt($framework, arch, plat, raw, fmt, exeopts) if not output fmt ||= "ruby" 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 # # These exception codes are fatal, we shouldn't expect them to succeed on the next # iteration, nor the next encoder. # rescue ::Errno::ENOENT, ::Errno::EINVAL $stderr.puts(OutError + "#{enc.refname} failed: #{$!}") break rescue => e $stderr.puts(OutError + "#{enc.refname} failed: #{e}") if verbose > 0 e.backtrace.each { |el| $stderr.puts(OutError + el.to_s) } end end } $stderr.puts(OutError + "No encoders succeeded.") end