When Java throws you a Lemon, make Limenade: Sandbox escape by type confusion

April 25, 2018 | Vincent Lee

Last week, Oracle released their quarterly Critical Patch Update (CPU). Seven of these bugs were submitted through the Zero Day Initiative (ZDI) program, and one of these bugs was quite reminiscent of the Java submissions in late 2012 and early 2013. The bug, CVE-2018-2826 (ZDI-18-307), is a sandbox escape vulnerability due to insufficient type checking discovered by XOR19. An attacker with low execution privileges may exploit this vulnerability to bypass the SecurityManager and escalate privileges.

The Vulnerability

The vulnerability lies in the implementation of reflection API, java.lang.invoke.MethodHandles::tryFinally​(MethodHandle target, MethodHandle cleanup) method. I’ll refer to this API as MethodHandles::tryFinally() hereafter. This API returns a MethodHandle that adapts a target method handle by wrapping it in a try-finally block. The cleanup method handle represents the functionality of the finally block. Any exception thrown during the execution of the target handle will be passed to the cleanup handle.

In the implementation of MethodHandles::tryFinally(), insufficient type checks were performed. It is possible to for an attacker to assign mismatched Throwable objects between target and cleanup MethodHandlers, resulting in a type confusion condition. The attacker in turn is able to cast any object into arbitrary types.

An Example

Suppose we have the following two classes and methods.

The attacker constructs a MethodHandle with throwEx() and handleEx()as cleanup. Notice that throwEx throws a Cast1-typed Throwable and handleEx() handles the Cast2-typed Throwable. When the attacker invokes the newly constructed MethodHandle in a vulnerable version of Java, it passes the Cast1 Throwable object into handleEx(), which handles Cast2-typed Throwable object without complaint or proper type checking. As handleEx() proceeds to process the Throwable, it handles the Lemon as a Lime, which allows the attacker to cast the Lemon into Lime and makeLimenade() out of Lemon.

The Exploit

In the exploit, the attacker attempts to bypass the Security Manager by setting it to null via reflection. First, the attacker obtains a methodHandle to a setter method on the security field of the System class via MethodHandles::publicLookup(). However, the security field is private and is thus normally inaccessible to MethodHandles::publicLookup(), so the attacker defines the following code and casts the Lookup object into a dummy LookupMirror object with the type confusion vulnerability:

In handleEx(), the attacker manipulates the Lookup object as LookupMirror object. From the relevant OpenJDK source code below, the attacker has changed the allowedModes property of the Lookup object into TRUSTED, allowing the attacker to obtain MethodHandles to arbitrary fields, including System.security, the SecurityManager field.

Finally, the attacker then obtains a method handle to the setter method of the System.security field using the trusted Lookup object and sets SecurityManager to null in order to bypass all restrictions imposed by a SecurityManger.

The Patch

Oracle patched this vulnerability by converting the object thrown by the target method handle into one that cleanup can handle. This ensures the MethodHandles::tryFinally() API will throw a WrongMethodTypeException when the types are mismatched and incompatible for explicit casting.

Conclusion

It’s interesting to see these Java type confusion bugs continue to be submitted to the program even though they aren’t used by exploits as much these days. Once browsers implemented “Click-to-Play,” practical exploitation became more difficult. This is also reflected in the amount of Oracle Java bugs submitted to the program over the years. That steep drop off coincides with the adoption of Click-to-Play and also illustrates how implementing mitigations to take out classes of vulnerabilities shifts researchers to other targets.

Java-related submissions to the ZDI program over time.

When my colleagues presented on Java exploits back in 2013, Oracle claimed more than 3 billion devices ran Java and that number is likely even higher now. Type confusion bugs were the most exploited vulnerability type back then, so it’s good to see these patched – even if they are never targeted.

You can find me on Twitter @TrendyTofu, and follow the team for the latest in exploit techniques and security patches.

Disclosure Timeline
·       2017-12-19 - Vulnerability reported to vendor
·       2018-04-18 - Coordinated public release of advisory