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.


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

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.


mihi said...

Did I see it correctly when looking at the new source, the CWE-694 flaw in the TransferrableProxy was not fixed at all? If I was Oracle, I'd have added a test for a duplicate classname in the map and thrown an exception (since serialization will never result in what you expect in that case...)

Anonymous said...

but... we NEED cut+paste to/from swing components from/to system clipboard. what to do now???
Many user of our chats in complain about this!

Sami Koivu said...

Anonymous: I haven't really looked at this from a perspective other than security. I imagine if you can sign your applet it will resolve the issue.