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