CAFEBABE

@CAFEBABE

Software Engineer.

Guestbook

Java: Serialization

Notes and Format

  • What: Convert an object into a stream of bytes by storing the state of the object.
  • What gets stored in the byte stream:
    • The member variables/fields
    • function signature (but NOT the function's code) impacts the bytes created for the object
  • How: "The default serialization mechanism for an object writes the class of the
    object, the class signature, and the values of all non-transient and
    non-static fields. References to other objects (except in transient or
    static fields) cause those objects to be written also. Multiple references
    to a single object are encoded using a reference sharing mechanism so that
    graphs of objects can be restored to the same shape as when the original was
    written."
  • How: "The default deserialization mechanism for objects restores the contents
    of each field to the value and type it had when it was written. Fields
    declared as transient or static are ignored by the deserialization process.
    References to other objects cause those objects to be read from the stream
    as necessary. Graphs of objects are restored correctly using a reference
    sharing mechanism. New objects are always allocated when deserializing,
    which prevents existing objects from being overwritten."

    "Reading an object is analogous to running the constructors of a new
    object. Memory is allocated for the object and initialized to zero (NULL).
    No-arg constructors are invoked for the non-serializable classes and then
    the fields of the serializable classes are restored from the stream starting
    with the serializable class closest to java.lang.object and finishing with
    the object's most specific class. "  

  • Important: With respect to byte streams, notes on closing the streams:
    • For an output stream, calling close() ensures that the data gets written to its destination (file or whathaveyou); not closing the stream will result in the data being in the buffer without being written to its destination.
    • For an input stream, a file descriptor is associated with the stream, and is stored in the file descriptor table; consequently, the file descriptor table can run out of space if there is a lot of unclosed file streams, and may stop the program from opening new files -- which can be problematic. Close your input file streams.
  • Important: it introduces vulnreability in the code since the serialized byte stream can be easily edited to change bytes such that some invariant of the serialized object is broken.
  • source https://docs.oracle.com/javase/7/docs/platform/serialization/spec/protocol.html#10258

Stream Format:

  • Stream Format For Classes

    Magic NumberVersion UIDclassNameserialVersionUIDhandleclassDescFlags bytefield1TypeCode field1NamefieldNTypeCode fieldNName <classNameForFieldOfObjectType>
    fieldBytes
    nullReferenceprevObjectReference

  • Stream Format For Objects        

Magic NumberVersion UIDclassNameserialVersionUIDhandleclassDescFlags bytefield1TypeCode field1NamefieldNTypeCode fieldNName <classNameForFieldOfObjectType>
fieldBytes

nullReference
prevObjectReference
classData[]: fieldsInClass, bytesForPrimitives

stream:
magic version contents



contents:
content contents content



content:
object: classes, outputStream Objects, Enums

blockdata: primitive dataTypes


  • "All of the write methods for primitive types encode their values using a DataOutputStream to put them in the standard stream format. The bytes are buffered into block data records so they can be distinguished from the encoding of objects. This buffering allows primitive data to be skipped if necessary for class versioning. It also allows the stream to be parsed without invoking class-specific methods."


Codes used in the serialized data:

primtypecode:

B = byte

C = char

D = double

F = float

I = integer

J =long

S = short

Z = boolean


objtypecode:

[ = array

L = object


final static short STREAMMAGIC = (short)0xaced;
    final static short STREAMVERSION = 5;
    final static byte TCNULL = (byte)0x70;
    final static byte TCREFERENCE = (byte)0x71;
    final static byte TCCLASSDESC = (byte)0x72;
    final static byte TCOBJECT = (byte)0x73;
    final static byte TCSTRING = (byte)0x74;
    final static byte TCARRAY = (byte)0x75;
    final static byte TCCLASS = (byte)0x76;
    final static byte TCBLOCKDATA = (byte)0x77;
    final static byte TCENDBLOCKDATA = (byte)0x78;
    final static byte TCRESET = (byte)0x79;
    final static byte TCBLOCKDATALONG = (byte)0x7A;
    final static byte TCEXCEPTION = (byte)0x7B;
    final static byte TCLONGSTRING = (byte) 0x7C;
    final static byte TCPROXYCLASSDESC = (byte) 0x7D;
    final static byte TC_ENUM = (byte) 0x7E;
    final static  int   baseWireHandle = 0x7E0000;



Serialization Vulnerability Demonstration Code:
<add after lunch>


You'll only receive email when CAFEBABE publishes a new post

More from CAFEBABE: