Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

CVE-2012-0507 describes an exploit that managed to bypass Java's applet security sandbox and run malicious code on a remote user's machine. The exploit deserialized a malicious object that subverted Java's type system. The malicious object was an array of two objects. The second object, of type AtomicReferenceArray<>, was contained an array containing array, but this array was also the first object. This data structure could not be created without serialization, because the array referenced by AtomicReferenceArray<> should be private. However, while the first object was an array of objects of type Help (which inherited from ClassLoader), the AtomicReferenceArray<>'s internal array type is was Object. This meant that the malicious code could use AtomicReferenceArray.set(ClassLoader) to create a Help object. (Creation of class loaders an object which was subsequently interpreted as being of type Help object, with no cast necessary. A cast would have caught this type mismatch. This allowed the attacker to create their own ClassLoader object, which is forbidden by the applet security manager.)

This exploit worked because in Java versions prior to 1.7.0_02 the object of type AtomicReferenceArray<> performed no validation on its internal array.

...

This exploit was mitigated in Java 1.7.0_03 by having the object of type AtomicReferenceArray<> validate its array upon deserialization. The readObject() method inspects the array contents, and if the array is of the wrong type, it copies makes a defensive copy of the array, foiling the exploit. (Note that this is an example of using rule This technique is recommended by OBJ06-J. Defensively copy mutable inputs and mutable internal components.)

Code Block
bgColor#ccccff
langjava
public class AtomicReferenceArray<E> implements java.io.Serializable {
  private static final long serialVersionUID = -6209656149925076980L;

  // Rest of class...

  /**
   * Reconstitutes the instance from a stream (that is, deserializes it).
   * @param s the stream
   */
  private void readObject(java.io.ObjectInputStream s)
    throws java.io.IOException, ClassNotFoundException {
    // Note: This must be changed if any additional fields are defined
    Object a = s.readFields().get("array", null);
    if (a == null || !a.getClass().isArray())
      throw new java.io.InvalidObjectException("Not array type");
    if (a.getClass() != Object[].class)
      a = Arrays.copyOf((Object[])a, Array.getLength(a), Object[].class);
    unsafe.putObjectVolatile(this, arrayFieldOffset, a);
  }
}

...