Wednesday, February 25, 2009

Java SE Security - Part I

I'm starting a section here outlining a few basic ways for analyzing Java SE security in a pure Java sense. That is: Java code which might escape from the security sandbox, most commonly set up for Java applets.

This first part will deal with the method AccessController.doPrivileged().

What does it do?

It is used to execute a piece of code in a privileged context. The different variations of the method take a PrivilegedAction, or PrivilegedExceptionAction instance as a parameter and execute the run method of that action.

Why do we need that?

All the code that comes from the jre/lib (rt.jar, etc) is already privileged. Even when executed in the context of an applet that code has the privileges to read/write files, open network connections, execute processes and so on.. as long as there is no untrusted code on the calling stack.

Wait, what? What does that mean?

All the code in the JRE that does stuff that is considered privileged is armed with a call to the SecurityManager to ask if the caller has sufficient privileges to call it. As an example, let's look at File.delete():
public boolean delete() {
SecurityManager security = System.getSecurityManager();
if (security != null) {
security.checkDelete(path);
}
return fs.delete(this);
}
So the first thing the method does is that it tries to obtain the currently installed SecurityManager and checks if it isn't null. Null in this case means there is no SecurityManager installed and everything is pretty much allowed. In the case of an applet, there will always be a SecurityManager, and next the code asks the SecurityManager if deleting at the given path is allowed. If the SecurityManager objects, it will throw a security exception. If no exception is thrown, the file actually gets deleted.

The SecurityManager checks every class that is on the calling stack (the method that called the method that called the method...) and makes sure every single of these classes is allowed to delete that file. So, if you try that from an applet's start method, that method will be on the calling stack and since your applet is not allowed to delete files, the SecurityManager will throw an exception.

So how does doPrivileged come into play?

There are some operations that have to be allowed, even though there is some untrusted code on the calling stack. A simple example is that many JRE methods require access to system properties that control their functionality. So the code that reads the system property is wrapped in a doPrivileged call and even if the method was executed from an applet, the operation is allowed.

This is actually intelligent. The potentially dangerous code which could introduce security vulnerabilities is wrapped up in and tagged by the doPrivileged blocks. This makes it pretty straightforward to audit them.

How does one find the doPrivileged blocks?

My preferred tool of the trade is Eclipse (http://www.eclipse.org/). To be effective, one needs the source code. The JDK normally comes with a src.zip, containing the source code of the public API classes and some other classes. However, to get some sources one needs to dig deeper.

Eclipse has a nice piece of functionality called "Call Hierarchy", which searches all the places where a given method is called, allowing you to dig deeper, viewing the callers' callers, and so on. So you could open the AccessController class, select the doPrivileged method and view it's call hierarchy.

How does one tell what's secure and what isn't?

Most of the doPrivileged blocks are small, precise, don't trust any input and do exactly one determined thing. However, this isn't always the case.

For example, public class sun.util.calendar.ZoneInfoFile has a private static method readZoneInfo file, which takes a filename String as a parameter and returns the file contents as a byte array. The file contents are read in a doPrivileged block.

Something like this:

package sun.util.calendar;

...

public class ZoneInfoFile {

...
private static byte[] readZoneInfoFile(String fileName) {

...
buffer = (byte[]) AccessController.doPrivileged(new PrivilegedExceptionAction() {

...
}
...

}


Things to consider:
  • It's a public class and therefore accessible outside it's package for all
  • It's not a serializable class
  • It's a non-final class, and it has an accessible constructor, so it could be subclassed.
  • It's in a sun. package and therefore cannot be accessed from an applet
  • The method is private and therefore cannot be invoked from another class

But:
  • The method is called from another private static method createZoneInfo of the same class
  • createZoneInfo is called from a public static method getZoneInfo (still inaccessible because of the package)
  • getZoneInfo is called from a public static method getTimeZone of the same class
  • getTimeZone is called from a private static method getTimeZone of the public class TimeZone (which resides in the accessible package java.util)
  • getTimeZone is called from a public static method getTimeZone of the public, accessible class java.util.TimeZone

So this path actually allows calling the doPrivileged block, passing any filename for it to read. However due to the long path, the return is no longer a byte array and there are a lot of validations along the way. In any case this illustrates the process of analyzing a piece of code.

No comments: