I managed to set up sort of a "Java sandbox" with the following code:
// #1
new File("xxx").exists();
// #2
PrivilegedExceptionAction<Boolean> untrusted = () -> new File("xxx").exists();
untrusted.run();
// #3
Policy.setPolicy(new Policy() {
@Override public boolean implies(ProtectionDomain domain, Permission permission) { return true; }
});
System.setSecurityManager(new SecurityManager());
AccessControlContext noPermissionsAccessControlContext;
{
Permissions noPermissions = new Permissions();
noPermissions.setReadOnly();
noPermissionsAccessControlContext = new AccessControlContext(
new ProtectionDomain[] { new ProtectionDomain(null, noPermissions) }
);
}
AccessControlContext allPermissionsAccessControlContext;
{
Permissions allPermissions = new Permissions();
allPermissions.add(new AllPermission());
allPermissions.setReadOnly();
allPermissionsAccessControlContext = new AccessControlContext(
new ProtectionDomain[] { new ProtectionDomain(null, allPermissions) }
);
}
// #4
try {
AccessController.doPrivileged(untrusted, noPermissionsAccessControlContext);
throw new AssertionError("AccessControlException expected");
} catch (AccessControlException ace) {
;
}
// #5
PrivilegedExceptionAction<Boolean> evil = () -> {
return AccessController.doPrivileged(untrusted, allPermissionsAccessControlContext);
};
try {
AccessController.doPrivileged(evil, noPermissionsAccessControlContext);
throw new AssertionError("AccessControlException expected"); // Line #69
} catch (AccessControlException ace) {
;
}
#1 and #2 should be self-explanatory.
#3 is the code that sets up the sandbox: It sets a totally unrestictive Policy
(otherwise we'd lock ourselves out immediately), and then a system SecurityManager
.
#4 then executes the "untrusted" code in a totally restrictive AccessControlContext
, which causes an AccessControlException
, which is what I want. Fine.
Now comes #5, where some evil code attempts to "escape" from the sandbox: It creates another, totally unrestricted AccessControlContext
, and runs the untrusted code within that. I would expect that this would throw an AccessControlException
as well, because the evil code successfully leaves the sandbox, but it doesn't:
Exception in thread "main" java.lang.AssertionError: AccessControlException expected
at sbtest.Demo.main(Demo.java:69)
From what I read in the JRE JAVADOC
doPrivileged
public static <T> T doPrivileged(PrivilegedExceptionAction<T> action, AccessControlContext context)
throws PrivilegedActionException
Performs the specified PrivilegedExceptionAction with privileges enabled and restricted by the
specified AccessControlContext. The action is performed with the intersection of the the permissions
possessed by the caller's protection domain, and those possessed by the domains represented by the
specified AccessControlContext.
If the action's run method throws an unchecked exception, it will propagate through this method.
Parameters:
action - the action to be performed
context - an access control context representing the restriction to be applied to the caller's
domain's privileges before performing the specified action. If the context is null,
then no additional restriction is applied.
Returns:
the value returned by the action's run method
Throws:
PrivilegedActionException - if the specified action's run method threw a checked exception
NullPointerException - if the action is null
See Also:
doPrivileged(PrivilegedAction), doPrivileged(PrivilegedExceptionAction,AccessControlContext)
, I would expect that the untrusted code would execute with no permissions and thus throw an AccessControlException
, but that does not happen.
Why?? What can I do to mend that security hole?