An example might be more useful.

Traditionally, when the first non serializable superclass zero argument constructor is called, the domains of the subclasses aren't present on the call stack. Any security checks performed in the constructor of the superclass will not include the subclasses domains.

In the proposed case, prior to construction, all domains in the class heirarchy of the to be deserialized object via the local ObjectStreamClass, are added to an AccessControlContext, which is then passed to the SecurityManager two argument permission check.

Now an attacker will not be able to construct this object unless all domains have DeserializationPermission.

If we use the stack context, it won't contain the yet to be deserialized classes.

Regards,

Peter.

On 11/02/2016 6:47 PM, Peter Firmstone wrote:
The use case here is:

Do we trust the code to deserialize unverified data from an untrusted source?

For example, an attacker might look for the following:

  1. Deserialization into privileged context
  2. Deserialization with a class that catches and ignores IOException.
  3. A hash collission that will cause equals to be invoked on a proxy
     and invocation handler.

History has shown us that no permission was required to perform these actions.

In this case, I'm not concerned who or which object might obtain a reference to some object that's serializable, because anyone can do so using a crafted stream reference.

An attacker can obtain a reference to any object in the stream using a stream reference.

Objects that shouldn't escape a permission check in a constructor or being returned from some method call containing a permission check, shouldn't be serializable.

What I'm concerned about is, do we trust all classes belonging to this object's heirarchy to read unverified data from the stream and verify it before creating an object?

Ideally the stream should be deserialized into a context that has no permission, but currently we have no way of restricting the attack surface to include only those classes necessary to perform some function.

We could use the traditional stack context of the calling thread, as you suggest.

It is true, an object that contains a field that didn't have deserialization permission, would itself become unserializable.

Regards,

Peter.


On 02/08/2016 10:19 PM, Peter Firmstone wrote:
>/ Why not, just prior to instantiating an object just prior to deserializing, add each class' ProtectionDomain in the objects hierarchy to an AccessControlContext and pass this to the SecurityManager's two argument checkPermission call?
/>/
/>/ This permission could never be granted to a principal, it is only ever a code trust concern. This would allow an administrator to minimise the attack surface of Serializable classes.
/
I think rather than using the ProtectionDomain of the objects in the
serialization stream, would it not make more sense to capture and use
(exclusively) the access control context of the entity which is
constructing the stream?  The reason I say this is that it's very
possible for a less-privileged object to have references to
more-privileged objects and vice-versa; also, in some cases you're
predicating the success of deserialization upon the order in which
objects are deserialized.

For example, if I have a four-object graph of A, B, C, and D, in a
diamond like this:

A->B
A->C
B->D
C->D

If B's class does not have privileges to construct D, deserialization
will fail, even if C does.  On the other hand, if B has permission to
construct D, but C doesn't, C can escape its restriction by relying on B
to have already deserialized the object.

But by using one permission set - the captured context of the creator of
the stream, or perhaps the captured context of the "root" readObject()
call, you cannot change the authorization outcome of the deserialization
just by fiddling with the bits.  A graph in this case is either valid or
not.

--
- DML

Reply via email to