Sunday, February 22, 2009

FileSystemView allows read access to the filesystem structure from an unsigned applet

(12/05/2008: Post deferred to give Sun time to fix the bug).

The bug was fixed in Java 6 update 11, released in the beginning of December 2008. Sun credited Henri Torgemane and yours truly, but I don't know Mr. Torgemane. I imagine we both reported the problem on different occasions around the same time.

Anyway, here's the original post:




I was looking at the Java applet security and file system access and seems like there's a problem in Java 6 update 6.

The class javax.swing.filechooser.FileSystemView allows listing of folder contents (to be very clear, by folder contents I mean the names of the files contained in that folder), starting from any ShellFolder (File subclass) instance.

ShellFolder instances are tricky to obtain, as access to the package is not allowed from an applet. Still, FileSystemView has two methods that return ShellFolder objects. The getRoots() and the getDefaultDirectory(). And the getFiles method returns an array of ShellFolder objects which can be used to recurse the whole filesystem. I was able to create an applet exploiting this on a Windows XP machine. The actual bug apparently was in the Win32ShellFolder2 class, which is the windows implementation for the low-level directory listing operations. The class overwrites a listFiles method and fails to ask the Security Manger if it's ok to list the files.

I originally started from an idea of an attack that involved constructing a FileDialog/JFileChooser and then having that dialog draw itself on an off-screen image. The plan was to then interpret this image an extract sensitive information from it.

Ran into some trouble with that approach. FileDialog, being an AWT component, refused to draw anything but a blank image on my image object and JFileChooser wouldn't instantiate, because the initialization tries to access the system property user.dir and the security manager won't accept that.

I proceeded to craft a subclass of JFileChooser, in which I overwrote the method that threw a security exception, calling the method of the superclass, but catching the exception, so that the init method would not fail and an instance would be created. This worked to some extent: the instance was created, a dialog window appeared, but it was empty. Nothing.

When thinking of ways to interact with the dialogs, I later thought of associating a FileFilter object with the dialog. I first tested with FileDialog, but even as I was writing the code, I read on the Javadoc that the Sun implementation doesn't take the FileFilter into consideration. I moved on to test with my crippled JFileChooser and bingo! it worked. For every file/folder in my "My Documents" folder, JFileChooser called the boolean accept(File) method of my FileFilter thus enabling me to capture the contents of that folder.

With that success, I then proceeded to try and change the folder of the JFileChooser to get access to other folders. No go. Whatever folder I set with JFileChooser.setCurrentDirectory, caused a security exception.

Not giving up so easily, I overwrote the getCurrentDirectory method to return a folder. To my surprise, that resulted in a security exception as well. That had me baffled. Why was the File object returned by the original JFileChooser working, and the one I created wasn't? I even tried to create a File with the same folder as the one that the JFileChooser returned by default. A security exception. I tried to call getParentFile() on the File object returned by the superclass. A security exception. What was going on? Then I discovered that the File instance that the getCurrentDirectory default implementation returned was actually a ShellFolder instance. ShellFolder is a subclass of File. I also discovered that the ShellFolder instance came from a class called FileSystemView that the JFileChooser uses for disk access. I found out that the FileSystemView has a getFiles(File, boolean) method which lists the contents of a folder. But calling it with a File object results in a security exception. Instantiating a ShellFolder object doesn't work, because accessing the sun.awt.shell package from an applet results in a security exception. But the FileSystemView methods getDefaultFolder and getRoots and getFiles return ShellFolder objects. In the end the solution was a lot simpler than what I was originally going for:

FileSystemView fsv = FileSystemView.getFileSystemView();

File[] roots = fsv.getRoots();
for (File root : roots) {

System.out.println(root.getPath());
}

Friday, February 13, 2009

Side-channel attack

I learned a new term the other day: Side-channel attack

While the term was new to me, the concept I was familiar with. It made me remember one of the smaller problems on Java applet security I had studied.

Untrusted applets have limited rights. There are lots of things they can't do. However, there is a ton of information about the execution environment that the applet does have access to, such as: Memory utilization, timing information, etc.

Consider, for example the method freeMemory of the Runtime class. The method can be called from an applet. Basically this method tells you how much free memory there is in the JVM. Garbage-collector and threads complicate, but basically you can use it find out how much memory a piece of code used. You'd do this by calling freeMemory before a block of code, and then immediately after the block of code, hoping the garbage collector didn't run and no other thread created objects, etc.

Consider now, a piece of privileged system code that you can execute, but it's been carefully crafted not to leak any privileged information to you.

But, in some cases, even the path that the code traverses is privileged information. You have no easy way to find out the path in a sandbox environment, but memory and timing information could be enough for a really good approximation.

Let's see a practical example:

import java.applet.Applet;
import java.util.TimeZone;

public class TZlet extends Applet {
public void start() {

for (int i=0; i < 1000; i++) {

long frees = Runtime.getRuntime().freeMemory();

TimeZone tz = TimeZone.getTimeZone("../../../../../../WINDOWS/notepad.exe");
long freee = Runtime.getRuntime().freeMemory();

System.out.println(frees-freee);
}
}

}


The getTimeZone method of the TimeZone class takes a String ID of a timezone. But, if there is no TimeZone object cached with the given ID, it reads and tries to parse a file named ID in java.home/lib/zi. So if we pass the ID "foo", it'll try to open java.home/lib/zi/foo, read the whole file into memory and then try to parse it.

From the example above, you might have guessed that it just concatenates the ID to a String which represents the path to the zi folder and does no sanity check for the ID. So we can pass ../../... until we get to the root of the drive and then put any path we please. In the case of the example: notepad.exe.

The parsing will most probably fail if you're not pointing to a zoneinfo file, so nothing useful will ever ever be returned from the method. But judging from how much memory was allocated, you can get a good approximation of the file size and test for file existence.

Wednesday, December 03, 2008

Calendar bug

Java 6 Update 11 fixes "calendar security bug" among others.

Altough some minor adjustments were made to the Calendar class, the actual fix deals with how deserialization calls non-serializable superclass constructor. But I like to call it the Calendar bug, anyway, because in my original evaluation of the problem I put the blame on the java.util.Calendar class.

The problem, which is present in Java 6 Update 10 and earlier (and in the corresponding Java 5 and Java 1.4.2 releases), goes something like this:

java.util.Calendar is a serializable class. Presumably due to reverse compatibility, its serialization logic is non-trivial. One of the non-trivial things that it does, is that the readObject method (which is used for customizing object deserialization) calls ObjectInputStream.readObject() inside a doPrivileged block to deserialize a ZoneInfo object which might or might not be present.

Something like this:
// If there's a ZoneInfo object, use it for zone.
try {
ZoneInfo zi = (ZoneInfo) AccessController.doPrivileged(
new PrivilegedExceptionAction() {
public Object run() throws Exception {
return input.readObject();
}
});
if (zi != null) {
zone = zi;
}
} catch (Exception e) {
}


doPrivileged blocks are used when the caller of trusted code doesn't necessarily have the privileges to execute some operation. In this specific case, the ZoneInfo object being deserialized resides in the sun.util.calendar package and applet code normally doesn't have access to this package (or any package which starts with "sun.").

Without the doPrivileged block, if an unsigned applet were to try to deserialize a Calendar object, it would raise a SecurityException. With doPrivileged, the code executes fine even when the caller (unsigned applet code) doesn't have the privileges. There is an old bug related to deserializing calendar objects and the doPrivileged block was presumably the fix.

The problem is that there is absolutely no guarantee that the stream being read contains a ZoneInfo object. It might contain, for example, a serializable ClassLoader subclass. One would have to manouver a bit to create such a stream, but with basic knowledge of the Java serialization protocol, this is a relatively trivial task.

So what happens if there should to be a Serializable ClassLoader subclass in the stream in the place of the ZoneInfo object? What's the big deal?

The input.readObject() method would execute just fine, because it has no knowledge of what type of an object it is supposed to deserialize. It reads the stream, gets the details of the next object in the stream, instantiates the object (without calling the constructor) and returns the object.

At this point the Calendar code tries to do a cast of the object to the ZoneInfo type and this will cause a ClassCastException, as the types aren't compatible. But the object was deserialized just fine. The implication of this is that applet code can deserialize objects in a privileged context, just by constructing an input stream (ByteArrayInputStream, for example) which contains a special serialized Calendar object which contains some other object instead of a ZoneInfo.

You might think that the reference to the newly created object is forever lost, only to be collected by the garbage collector later on. However, it is possible to implement a readObject method in the serializable class which stores the reference to the created object in some static context.

So what?

Here's where the ClassLoader comes into play. java.lang.ClassLoader is not Serializable and thus can't be serialized. But it is a non-final class, so it can be extended.

Subclasses of non-serializable classes can be serializable just by implementing the java.io.Serializable interface. When the object is deserialized, the JVM first calls the constructor of the non-serializable class, and then it deserializes the fields of the serializable subclass. The fancy part is that there will be no subclass code on the stack when the constructor of the non-serializable class is called (or rather, this was true until update 11). Thus, if the deserialization happens in a privileged context, the constructor also gets called in a privileged context.

Why is this relevant?

Because calling a ClassLoader constructor requires special privileges. Privileges that an unsigned applet normally doesn't have. So this way an applet could (past tense, because of the fix in update 11) obtain it's very own ClassLoader. If you have access to your own ClassLoader subclass, you can load new classes with any privileges. You can load a class which has the privileges to do anything that the user running the browser running the applet can do, including, but not limited to, removing files, opening connections and executing external programs.

So how did Sun fix the bug?

Well, first of all, they left the doPrivileged block in the Calendar class. I'm not a 100% percent sure that was a correct decision, but more about that later on.

What they did fix, and on this I fully agree is that now when a serializable subclass of a non-serializable class is deserialized, a generated sun.reflect.GeneratedSerializationConstructorAccessorxxx instance is put on the call stack before calling the superclass constructor, thus making it lose the privileged context in the case where the subclass itself isn't privileged.

I had to correct myself on this one.

Sunday, May 11, 2008

FileSystemView allows read access to the filesystem structure from an unsigned applet

(12/05/2008: Post deferred to give Sun time to fix the bug).

The bug was fixed in Java 6 update 11, released in the beginning of December 2008. Sun credited Henri Torgemane and yours truly, but I don't know Mr. Torgemane. I imagine we both reported the problem on different occasions around the same time.

Anyway, here's the original post:




I was looking at the Java applet security and file system access and seems like there's a problem in Java 6 update 6.

The class javax.swing.filechooser.FileSystemView allows listing of folder contents (to be very clear, by folder contents I mean the names of the files contained in that folder), starting from any ShellFolder (File subclass) instance.

ShellFolder instances are tricky to obtain, as access to the package is not allowed from an applet. Still, FileSystemView has two methods that return ShellFolder objects. The getRoots() and the getDefaultDirectory(). And the getFiles method returns an array of ShellFolder objects which can be used to recurse the whole filesystem. I was able to create an applet exploiting this on a Windows XP machine. The actual bug apparently was in the Win32ShellFolder2 class, which is the windows implementation for the low-level directory listing operations. The class overwrites a listFiles method and fails to ask the Security Manger if it's ok to list the files.

I originally started from an idea of an attack that involved constructing a FileDialog/JFileChooser and then having that dialog draw itself on an off-screen image. The plan was to then interpret this image an extract sensitive information from it.

Ran into some trouble with that approach. FileDialog, being an AWT component, refused to draw anything but a blank image on my image object and JFileChooser wouldn't instantiate, because the initialization tries to access the system property user.dir and the security manager won't accept that.

I proceeded to craft a subclass of JFileChooser, in which I overwrote the method that threw a security exception, calling the method of the superclass, but catching the exception, so that the init method would not fail and an instance would be created. This worked to some extent: the instance was created, a dialog window appeared, but it was empty. Nothing.

When thinking of ways to interact with the dialogs, I later thought of associating a FileFilter object with the dialog. I first tested with FileDialog, but even as I was writing the code, I read on the Javadoc that the Sun implementation doesn't take the FileFilter into consideration. I moved on to test with my crippled JFileChooser and bingo! it worked. For every file/folder in my "My Documents" folder, JFileChooser called the boolean accept(File) method of my FileFilter thus enabling me to capture the contents of that folder.

With that success, I then proceeded to try and change the folder of the JFileChooser to get access to other folders. No go. Whatever folder I set with JFileChooser.setCurrentDirectory, caused a security exception.

Not giving up so easily, I overwrote the getCurrentDirectory method to return a folder. To my surprise, that resulted in a security exception as well. That had me baffled. Why was the File object returned by the original JFileChooser working, and the one I created wasn't? I even tried to create a File with the same folder as the one that the JFileChooser returned by default. A security exception. I tried to call getParentFile() on the File object returned by the superclass. A security exception. What was going on? Then I discovered that the File instance that the getCurrentDirectory default implementation returned was actually a ShellFolder instance. ShellFolder is a subclass of File. I also discovered that the ShellFolder instance came from a class called FileSystemView that the JFileChooser uses for disk access. I found out that the FileSystemView has a getFiles(File, boolean) method which lists the contents of a folder. But calling it with a File object results in a security exception. Instantiating a ShellFolder object doesn't work, because accessing the sun.awt.shell package from an applet results in a security exception. But the FileSystemView methods getDefaultFolder and getRoots and getFiles return ShellFolder objects. In the end the solution was a lot simpler than what I was originally going for:

FileSystemView fsv = FileSystemView.getFileSystemView();

File[] roots = fsv.getRoots();
for (File root : roots) {

System.out.println(root.getPath());
}

Saturday, May 10, 2008

Security is hard

Security is hard.

When you're trying to build something secure, you have to consider everything.

When you're breaking the security, you just have to think of one thing that the other guy didn't think of. Not to mention the person creating security is normally vastly outnumbered.

I like security. I like locks (designing locks and dreaming up ways to defeat them). I like security protocols (analyzing them anyway, following them can be a pain). I like security related algorithms (encryption, one-way hashing, etc). I like security in software development.

These days I've been revisiting the security of Java's sandbox while running in applets. It's wildly intriguing, because of the complexity involved. Even a simple login screen can be complicated to get just right, in terms of security. And the applet sandbox is a whole different beast. The applets need to be able to do almost anything.. and then there are a few, handful of things that the applet absolutely must not be able to do. Add to the mix Java's system of access modifiers, inheritance, threads, serialization, bytecode, etc. It's a complicated thing. I can't believe they've got it 100% right. Can't believe it. It doesn't seem probable. So often, when I have spare time I go about looking at the Java core code, doing tests, trying to be creative.

Tuesday, May 22, 2007

Minimizing Java bytecode size

I played around with the topic mentioned in the title of this entry when I was considering entering the Java 4K Programming Contest. I ended up not participating in the contest because I couldn't dream up any cool ideas for a game, but I did come up with a list of things which help with the code size. I would like to point out that the 4K limit is for the executable class file and not the source file and thus these actions have the intention of reducing the size of the compiled class file.

Some of these are obvious. Others might be less obvious to the masses.

Action: Don't bother with generics.
Justification: Generics stuff is not used by the JVM and it only takes up space.

Action: Compile without debug information or strip it afterwards.
Justification: This includes source code linenumber information, source code file name and localvariable names. The attributes in the class file to be removed are Source, LocalVariableTable and LineNumberTable. These are not required as they only exist for debugging purposes.

Action: Rename all class, method and field names to be one character.
Justification: One character takes up less space than two or three or four.

Action: Do not define a package.
Justification: The package definition provides no functionality (in this context) and takes up space.

Action: Minimize the number of methods.
Justification: Method headers take up a lot of space.
2 (accessflags) +
2 (name index) +
2 (descriptor index) +
20 (empty Code attribute)

That's 26 bytes and still assuming you manage to reuse the name string in the constant pool and reuse the method descriptor (which defines the return type and parameters) and not throw any exceptions.

Action: Minimize the number of fields.
Field headers take up a lot of space.
2 (accessflags) +
2 (name index) +
2 (descriptor index) +
2 (0 attributes)

That's 8 bytes and still assuming you manage to reuse the name string in the constant pool and manage to reuse the descriptor.

Action: Strip any method throws information.
Justification: The VM doesn't use this information. You can't tell the compiler not to create it, but it can be stripped afterwards.

Action: Rearrange local variables, putting the four that are the most used first. Heavily reuse these variables.
Justification: Instructions that refer to the local variables 0-3 take up one byte and instructions that refer to the rest of the local variables take up two bytes.

Action: Set the scope of the local variables so that only 4 variables are in scope at any given time.
Justification: Two variables, even if they're of different types, can be stored in the same local variable "slot" if their scopes don't overlap. And why using the first four local variables is good is explained in the previous item.

Action: Reuse string constants. As well as any string literals in the code, this includes class, field and method-names. If you have one class which has one field and the main method (to have a Java application entry point) name the class and the field "main" as well.
Justification: The compiler will only need to put one string entry into the class file constant pool.

Thursday, March 01, 2007

Java 6 == NoClassDefFoundError

I thought I might as well share this.

Problem

I'm working on an open source project and recently I tried to run the executable .jar file on my windows. It wouldn't run, which was surprising since I had done some basic testing on it and I had certainly been able to run it before.

That version still had no logging support, so I went to the console to try and run it, and sure enough it died with a:
java.lang.NoClassDefFoundError: com.sun.jdi.StackFrame

Background


That com.sun.jdi package quickly gave a pretty good idea about what was going on. Time for some background information.

First of all, the project had no external dependencies. I wasn't using anything that required extra jars to be packaged with the binary. This wasn't really as much a conscious decision as it was just something that happened.

When I added support for debugging another VM to my project, I used the com.sun.jdi.* classes from tools.jar that comes in a standard JDK. I had the bright idea to still not introduce a forced dependency, making this functionality optional: If the classes were found in the classpath the functionality would be there, if not, the rest of the application would still be usable.

I did this in a somewhat lazy and half-hearted, experimental manner, just testing around until it worked the way I wanted.

Reason for problems

So when I got that NoClassDefFoundError I knew my fragile construction had broken down. I initially assumed it had something to do with the changes to the Java 6 class verification, but instead it turned out that it was about changes to Swing classes: I had a JPanel subclass which had a method which had that JDI class in it's signature, and in Java 6 a call to the JPanel's constructor does something like:

this.getClass().getDeclaredMethods();

And that bit of reflection causes the classloader to try and verify all the classes referenced in the signatures of the methods (with any version of Java).

Fix

Having learned my lesson, I wrapped all the references to the JDI classes in any method signatures in my own local interfaces and now the project once again works fine with Java 6 as well.

Is this your problem?


I don't think that a lot of sensible people would try something like my original unplanned bubblegum attempt, but I do see some other possible scenarios where the same problem might arise. They all involve an existing system with some problems which are triggered by a change to Java 6.

For example, you might have a reusable component A which is compiled separately and has some dependencies to an external library B. The component A is used by two systems C and D. System C uses functionality which requires the library B and System D uses functionality that doesn't require the library B. For some reason library B hasn't been added to the classpath of the System D and it's never caused a problem before, but there's a reference to one of the classes in library B in some signatures of a user interface class and thus moving to Java 6 triggers that problem. Easy enough to fix adding the library B to the classpath, but a very annoying problem in the case of a maintenance system whose inner workings nobody is very familiar with.