Versions Compared

Key

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

Unrestricted deserializing from a privileged context allows an attacker to supply crafted input which, upon deserialization, can yield objects that the attacker does not have permissions to construct. Construction of a sensitive object such as a custom class loader is one example (See SEC12-J. Do not grant untrusted code access to classes existing in forbidden packages and SEC13-J. Do not allow unauthorized construction of classes in forbidden packages).

...

A non-serializable class can be extended and its subclass can be made serializable, or it the subclass becomes serializable automatically if it derives from a serializable class. During deserialization of the subclass, the JVM calls the no-argument constructor of the most derived superclass that does not implement java.io.Serializable either directly or indirectly. This allows it to fix the state of this most derived superclass that does not implement Serializable. In the code snippet that immediately follows, class A's no-argument constructor is called when C is deserialized because A does not implement Serializable. Subsequently, Object's constructor is invoked. This procedure cannot be carried out programmatically, consequently the JVM generates the equivalent bytecode at runtime. Typically, when the superclass's constructor is called by a subclass, the subclass remains on the stack. However, in deserialization this does not happen. Only the unvalidated bytecode is present. This allows any security checks within the superclass's constructor to be bypassed, in that, the complete execution chain is not brought into scrutiny.

Code Block
class A { // has Object as superclass
  A(int x) { }
  A() { }
}

class B extends A implements Serializable {
  B(int x) { super(x); }
}
 
class C extends class B {
  C(int x) { super(x); }
}    

...

Code Block
bgColor#FFcccc
try {
  ZoneInfo zi = (ZoneInfo) AccessController.doPrivileged(
    new PrivilegedExceptionAction() {
      public Object run() throws Exception {
        return input.readObject();
      }
  });
  if (zi != null) {
    zone = zi;
  }
 } catch (Exception e) {
 }

Compliant Solution

This vulnerability was fixed in JDK v1.6 u11 by defining a new AccessControlContext INSTANCE, with a new ProtectionDomain. The ProtectionDomain encapsulated a RuntimePermission called accessClassInPackage.sun.util.calendar. Consequently, the code was granted the minimal set of permissions required to access the sun.util.calendar class. This whitelisting approach guaranteed that a security exception would be thrown in all other cases of invalid access. Refer to SEC12-J. Do not grant untrusted code access to classes existing in forbidden packages for more details on allowing or disallowing access to packages.

Code Block
bgColor#ccccff
private static class CalendarAccessControlContext {
  private static final AccessControlContext INSTANCE;
    static {
      RuntimePermission perm = new RuntimePermission("accessClassInPackage.sun.util.calendar");
      PermissionCollection perms = perm.newPermissionCollection();
      perms.add(perm);
      INSTANCE = new AccessControlContext(new ProtectionDomain[] {
        new ProtectionDomain(null, perms)
      });
    }
  }

// ...
try {
zi = AccessController.doPrivileged(
       new PrivilegedExceptionAction<ZoneInfo>() {
         public ZoneInfo run() throws Exception {
           return (ZoneInfo) input.readObject();
         }
       }, CalendarAccessControlContext.INSTANCE);
} catch (PrivilegedActionException pae) { /* ... */ }
} catch (Exception e) { }
if (zi != null) {
  zone = zi;
}

The two-argument form of doPrivileged() allows stripping all permissions other than the ones specified in the ProtectionDomain. Refer to SEC00-J. Follow the principle of least privilege for more details on using the two-argument doPrivileged() method.

...