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