class Rex::Exploitation::RopDb
This class provides methods to access the ROP database, in order to generate a ROP-compatible payload on the fly.
Public Class Methods
# File lib/rex/exploitation/ropdb.rb, line 15 def initialize @base_path = File.join(File.dirname(__FILE__), '../../../data/ropdb/') end
Public Instance Methods
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.
# File lib/rex/exploitation/ropdb.rb, line 77 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 == :unsafe_negate_size get_unsafe_size(payload.length) elsif e == :safe_negate_size get_safe_size(payload.length) else e end }.pack("V*") raise RuntimeError, "No ROP chain generated successfully" if rop.empty? return pivot + rop + payload end
Returns true if a ROP chain is available, otherwise false
# File lib/rex/exploitation/ropdb.rb, line 25 def has_rop?(rop_name) File.exists?(File.join(@base_path, "#{rop_name}.xml")) 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, :unsafe_negate_size, and :safe_negate_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.
# File lib/rex/exploitation/ropdb.rb, line 40 def select_rop(rop, opts={}) target = opts['target'] || '' base = opts['base'] || nil raise RuntimeError, "#{rop} ROP chain is not available" if not has_rop?(rop) xml = load_rop(File.join(@base_path, "#{rop}.xml")) gadgets = [] xml.elements.each("db/rop") { |e| name = e.attributes['name'] 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 gadgets << parse_gadgets(e, base) } return gadgets.flatten end
Private Instance Methods
Returns a size that's safe from null bytes. This function will keep incrementing the value of āsā until it's safe from null bytes.
# File lib/rex/exploitation/ropdb.rb, line 114 def get_safe_size(s) safe_size = get_unsafe_size(s) while (safe_size.to_s(16).rjust(8, '0')).scan(/../).include?("00") safe_size -= 1 end safe_size end
Returns a size that might contain one or more null bytes
# File lib/rex/exploitation/ropdb.rb, line 127 def get_unsafe_size(s) 0xffffffff - s + 1 end
Checks if a ROP chain is compatible
# File lib/rex/exploitation/ropdb.rb, line 135 def has_target?(rop, target) rop.elements.each('compatibility/target') { |t| return true if t.text =~ /#{target}/i } return false end
Returns the database in XML
# File lib/rex/exploitation/ropdb.rb, line 145 def load_rop(file_path) f = File.open(file_path, 'rb') xml = REXML::Document.new(f.read(f.stat.size)) f.close return xml end
Returns gadgets
# File lib/rex/exploitation/ropdb.rb, line 156 def parse_gadgets(e, image_base) gadgets = [] 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 << (image_base + addr.to_i(16)) elsif value case value when 'nop' gadgets << :nop when 'junk' gadgets << :junk when 'size' gadgets << :size when 'unsafe_negate_size' gadgets << :unsafe_negate_size when 'safe_negate_size' gadgets << :safe_negate_size else gadgets << value.to_i(16) end else raise RuntimeError, "Missing offset or value attribute in '#{name}'" end } return gadgets end