Another Web Site Threat?

I had the good fortune to meet a gentlemen by the name of Jeff Williams a few months back. He was in the Chicagoland area sponsoring an OWASP event that attracted thousands of people. Prior to attending the event, I knew Jeff by name but never had the experience of meeting and talking to him. Jeff was also in town co-sponsoring an event with my company promoting his company, Contrast Security. Fore those of you who do not know Jeff, he is a founder of the OWASP community as well as several successful companies, including Contrast.

The event was very enlightening in many ways. I always thought that once I deployed a WAF, my Wed Site was secure. What I didn’t know is only as good as the gate to your CDN or POP. I was missing out on the dynamics of a OWASP product fixed at the very gate of my Web presence – my Web servers. In this case a multitude of Apache front-end servers.

With that said, I wanted to share one of Jeff’s recent posts with respect to the Java Serialization Vulnerability. If you find this post interesting, please visit my Web Site, Candor Technology Solutions. I would love to hear from you!

Best,

Doug

________________________________________________________

A widespread vulnerability in Java environments leaves thousands of businesses seriously exposed. Despite lacking a clever name — ala Heartbleed, Shellshock, and POODLE — this vulnerability is poised to allow hackers to do damage across the Internet. Any application that accepts serialized Java objects is likely vulnerable, even if a framework or library is responsible and not your custom code. And there’s no easy way to protect applications en-masse. That means it will take organizations a long time to find and fix all the different variants of this vulnerability.
Contrast Security has a solution that uses our patented, powerful application security instrumentation platform to find and fix this problem both quickly and accurately. Contrast can identify this problem during development using our IAST (Interactive Application Security Testing) approach. And Contrast can also protect applications in production using our RASP (Runtime Application Self Protection) features to patch the problem immediately or generate security alerts, with no re-coding necessary. One Contrast agent protects all applications on a server, so it’s easy to protect your entire portfolio against serialization attacks as well as a broad array of other vulnerabilities and attacks.
To learn more about the details of the Java serialization flaw, keep reading. Serialization is a way that developers turn their data structures into a stream of bytes for transport or storage. Deserialization is the reverse process that happens when the data is received. There have been security issues with serialization for a long time – going back to 2010 and before. See http://www.ibm.com/developerworks/library/se-lookahead for the full history. Just recently, exploits were published for a bunch of different Java environments — which unfortunately appears to be the only way to make something happen in security.
These flaws are serious and can be used to effect a complete remote command execution — total host takeover on any application that accepts serialized objects.
In Java, reading a Data object from a serialized stream is as simple as:
1. ObjectInputStream in = new ObjectInputStream( inputStream );
2. return (Data)in.readObject();
The problem is that there’s no way to know what you’re deserializing before you’ve decoded it. So an attacker can serialize a bunch of malicious objects and send them to your application. Once you call readObject(), it’s too late. The attackers malicious objects have already been instantatiated, and have taken over your entire server. In some ways it’s like an XXE problem, where an attacker can use a malicious DOCTYPE to generate attacks during XML parsing. But in this case, there’s no easy way to turn off DOCTYPE processing.
What’s needed is a way to allow deserialization, but make it impossible for attackers to create instances of arbitrary classes. Something like this:
1. // read in the serialized object SAFELY
2. List<Class<?>> safeClasses = Arrays.asList( BitSet.class, ArrayList.class );
3. Data data = safeReadObject( Data.class, safeClasses, 10, 50, inputStream );
This allows the developer to specify the return type and a list of the classes that they expect to show up in serialized objects. The prevents the attacker from loading classes that allow him to execute attacks. This also empowers the developer to limit the input to a maximum of 10 embedded objects and 50 bytes of input. This way, the attacker can’t send an arbitrarily large serialized object to blow up your JVM by exhausting memory.
When something unauthorized shows up, we throw a SecurityException and block the attempt. Turns out that it’s not too difficult to implement all these defenses. We just need to overload a bit of the ObjectInputStream implementation.
Here’s a method that you can use to replace calls to readObject:
1. /**
2. * A method to replace the unsafe ObjectInputStream.readObject() method built into Java. This method
3. * checks to be sure the classes referenced are safe, the number of objects is limited to something sane,
4. * and the number of bytes is limited to a reasonable number. The returned Object is also cast to the
5. * specified type.
6. *
7. * @param type Class representing the object type expected to be returned
8. * @param safeClasses List of Classes allowed in serialized object being read
9. * @param maxObjects long representing the maximum number of objects allowed inside the serialized object being read
10. * @param maxBytes long representing the maximum number of bytes allowed to be read from the InputStream
11. * @param in InputStream containing an untrusted serialized object
12. * @return Object read from the stream (cast to the Class of the type parameter)
13. * @throws IOException
14. * @throws ClassNotFoundException
15. */
16. @SuppressWarnings(“unchecked”)
17. public static T safeReadObject(Class<?> type, List<Class<?>> safeClasses, long maxObjects, long maxBytes, InputStream in ) throws IOException, ClassNotFoundException {
18.
19. // create an input stream limited to a certain number of bytes
20. InputStream lis = new FilterInputStream( in ) {
21. private long len = 0;
22. public int read() throws IOException {
23. int val = super.read();
24. if (val != -1) {
25. len++;
26. checkLength();
27. }
28. return val;
29. }
30. public int read(byte[] b, int off, int len) throws IOException {
31. int val = super.read(b, off, len);
32. if (val > 0) {
33. len += val;
34. checkLength();
35. }
36. return val;
37. }
38. private void checkLength() throws IOException {
39. if (len > maxBytes) {
40. throw new SecurityException(“Security violation: attempt to deserialize too many bytes from stream. Limit is ” + maxBytes);
41. }
42. }
43. };
44.
45. // create an object input stream that checks classes and limits the number of objects to read
46. ObjectInputStream ois = new ObjectInputStream( lis ) {
47. private int objCount = 0;
48. boolean b = enableResolveObject(true);
49. protected Object resolveObject(Object obj) throws IOException {
50. if ( objCount++ > maxObjects ) throw new SecurityException( “Security violation: attempt to deserialize too many objects from stream. Limit is ” + maxObjects );
51. Object object = super.resolveObject(obj);
52. return object;
53. }
54. protected Class<?> resolveClass(ObjectStreamClass osc) throws IOException, ClassNotFoundException {
55. Class<?> clazz = super.resolveClass(osc);
56. if (
57. clazz.isArray() ||
58. clazz.equals(type) ||
59. clazz.equals(String.class) ||
60. Number.class.isAssignableFrom(clazz) ||
61. safeClasses.contains(clazz)
62. ) return clazz;
63. throw new SecurityException(“Security violation: attempt to deserialize unauthorized ” + clazz);
64. }
65. };
66.
67. // use the protected ObjectInputStream to read object safely and cast to T
68. return (T)ois.readObject();
69. }
This method overrides the resolveClass() method in ObjectInputStream and adds checks to make sure that any class loaded as part of the deserialization process is either not exploitable or contained in the whitelist of safe classes. As an alternative, you could also blacklist the classes you don’t know — the ones known to be used in exploits — but this approach is doomed to failure. There are simply too many so-called “gadgets” on the classpath of a modern application to effectively list all the possible exploits.
This simple check protects your applications without radical changes to your code. The first step is to search your code to find all the places that you’re vulnerable and update them with calls to safeReadObject(). However, this approach doesn’t protect you if the unsafe calls to readObject() are in your libraries or frameworks. The best way to implement full protection is with instrumentation, and tomorrow, all Contrast users will receive an update that does just that. You’ll be notified all the places across your application portfolio where you’re exposed due to deserializing untrusted data!
Good luck!

Leave a comment