java动态代理(RMI 基础)

目录

javaRMI基础

服务端

RemoteObj,继承Remote,定义了一个接口

 RemoteObjImpl,实现了接口

RMIServer,创建注册中心默认端口1099,然后绑定对象

客户端

RMIClient,锁定代理中心然后查询对象名

从 IDEA 断点分析 RMI 通信原理 

创建注册中心

绑定的流程

 服务端的流程

 获取远程对象

 客户端连接服务端

 ​编辑

客户端请求注册中心(客户端攻击注册中心)

只要创建了远程对象就会生成DGC,DGC的服务端和客户端都可以被攻击。

高版本jdk绕过


 

java动态代理其实就是方便了开发人员的工作量, 不需要一层一层的修改代码。

一般来说会有四个类来实现,

  1. 接口类 FileSystemProxyTest.java
  2. 实现接口类  UnixFileSystem.java
  3. 实现注解类 JDKInvocationHandler
  4. 主类   FileSystemProxyTest.java
UnixFileSystem fileSystem = new UnixFileSystem();//new的实现接口的类
        // 使用JDK动态代理生成FileSytem动态代理类实例
        FileSystem proxyInstance=(FileSystem) Proxy.newProxyInstance(
                FileSystem.class.getClassLoader(),//指定动态代理类的类加载器
                new Class[]{FileSystem.class},//定义动态代理生成的类实现的接口
                new JDKInvocationHandler(fileSystem)//动态代理处理类
        );

其实关键信息就是这几行。

javaRMI基础

前提:JDK<=8u121

RMI全称(远程方法调用),即在一个JVM中java程序调用在另一个远程JVM中运行的java程序,两者之间通过网络进行通信。

RMI 依赖的通信协议为 JRMP(Java Remote Message Protocol,Java 远程消息交换协议),该协议为 Java 定制,要求服务端与客户端都为 Java 编写。

RMI包括三部分

Server----------服务端:服务端通过绑定远程对象,这个对象可以封装很多网络操作,Socket

Client----------客户端:客户端调用服务端的方法

Registry-------注册端:提供服务注册与服务获取。

先简单的创建一个Demo

服务端

RemoteObj,继承Remote,定义了一个接口

package com.cxk.RMI;

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

public interface RemoteObj extends Remote{
    public String sayHello(String keywords) throws RemoteException;
}

 RemoteObjImpl,实现了接口

package com.cxk.RMI;

import com.sun.org.apache.xpath.internal.compiler.Keywords;

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

public class RemoteObjImpl extends UnicastRemoteObject implements RemoteObj{
    protected RemoteObjImpl() throws RemoteException {
    }

    public String sayHello(String keywords) throws RemoteException {
        String upKeywords = keywords.toUpperCase();
        System.out.println(upKeywords);
        return upKeywords;
    }
}

RMIServer,创建注册中心默认端口1099,然后绑定对象

package com.cxk.RMI;

import java.rmi.AlreadyBoundException;
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 {
        RemoteObjImpl remoteObj = new RemoteObjImpl();//new一个实现类,好像动态代理  实例化远程对象
        Registry registry = LocateRegistry.createRegistry(1099);//绑定对象示例到注册中心
        registry.bind("remoteObj",remoteObj);
    }
}

客户端

RMIClient,锁定代理中心然后查询对象名

package com.cxk.RMI;


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 {
        Registry registry = LocateRegistry.getRegistry("127.0.0.1", 1099);
        RemoteObj remoteObj = (RemoteObj) registry.lookup("remoteObj");
        remoteObj.sayHello("hello");
    }
}

服务端和客户端同时运行,发现结果输出在服务端,不难理解因为我们在客户端实现的功能其实就是远程操作服务端。

ps :实现类的接口,发现没 implements接口,一直报错代理错误,搞了一个小时 md气死

从 IDEA 断点分析 RMI 通信原理 

首先会调用父类的方法, port=0发送到一个随机的端口,然后调用exportObject

7f9b76204ce04072823ffa05fd8a27c8.png

这里的obj是我们传入的对象,前面函数是实现接口的方法,猜测后面的是处理网络接口的 

f59e5e26b3ba47148d7477b6bb4838ce.png 跟进new LiveRef中去

65a2ee4780bf4532ba0de4c2f8b38e6c.png

 跟进this看一下构造函数f61cd18a08d94eabae3bde4a40329139.pngf9bd5c7aebb442789877524bbfacce85.png

 之后就是各种封装,感觉不太重要就不 记录了。

创建注册中心

首先创建注册中心,默认端口为1099 

44fbadf067834118ad80b65e0518993e.png

 15ab9fe08ba34d38ad6720e623113b54.png

 这里发现最后一个值为true,而我们上面调用的是false,true的作用说明是永久的83920a74403f4ad983e2e7b1d3b01f41.png 跟进exportObject

abbf5016b3ca4ef894dd79e33c1a5be9.png

 然后里面有判断跟进去看以下,2d1ec31d3f6d48999ea9dd506eae0046.png

 如果当前文件名+_Stub在系统中存在就返回true,这里的remoteClass=Registrylmpl,系统中存在Registrylmpl_stub是反编译出来的。5566eb0ea3a045d4a69b8819269fb5a9.png

 满足条件调用createStub方法f0ec260b8f2441df85f91b6baf1458a9.png

 获取类名然后获取构造器,d63e1e11d5ef4d5e88ed3387d432ece2.png

 返回回来发现stub5ee135487d0e4be3a88d3a89497a8735.png

 82bfd99c05244319b1ff4ffa5b7985f9.png

 然后用Target封装我们有的东西

绑定的流程

089ad61070a24749b215d0251caabc40.png

bindings.get就是里面看有没有重复的名字。checkAccess限制了本地 

 服务端的流程

第一步需要从注册中心获取代理的远程对象

第二步通过代理向服务端获取调用。

首先

LocateRegistry.getRegistry,getRegistry也很神奇,这里采用了生成liveRef本地对象

e03db4c67952450f90d83252e1373665.png

 之后的操作和创建服务端一模一样 ,也就是说,客户端并不是直接运用服务端的东西,而是获取对象在客户端自己创建了一个,9aa5f5faf1474565bba7327d1d4b4b7e.png

 客户端自己 创建的对象,f0f98ca98376478b8ac9c533097cf2e8.png

 获取远程对象

90563f592d2b4f1dbb785f2a9b3235b9.png

 传一个字符串进来就是在注册中定义的名字,然后进行序列化输出,这里注册中心肯定有反序列化 的点

里面会调用一个executeCall()方法

51859157cff6471cbe8faea0550155d7.png

 是一个真正处理网络请求的方法,客户端的请求都是它来处理

a3ac0a847a524e929f1275710ae96ed8.png

 通过反序列化来获取代理,客户端向注册中心获取代理的过程是通过反序列化得来的,如果有一个恶意的注册 中心,那么就可以危害客户端a9d03d39c801420e8450ae92a4059170.png

 在executecall()方法中有反序列化的点,如果注册中心返回一个恶意的流,这个危害性很高因为很多方法都会调用它,因为它是处理 网络请求的,所以很多人访问ff8f4e13be0d468e85e23e33c5a974e4.png

 发现 invoke中就调用了executeCall()方法,所以只要调用invoke都有可能有漏洞,所以客户端很危险17a523217aa14db492f9bc9aad58d516.png

 7f75e851fdd340d5a0d15ecd216dee75.png

 客户端连接服务端

remoteObj.sayHello("hello");

这里直接进入了invoke方法

063e1ea1e1614ceaaff4ff995fde86d0.png

 因为remoteObj是代理对象,代理不论调用什么都会先调用invoke方法

marshalValue会序列化对象的 值

fc6a508ed6e64d609045bad32152aaf2.png

 会把参数值序列化进去f3b08d7b331f4fc6a69aa44f990c0dee.png

 在下面又调用了executeCall,3eb2e40a28ac443a8a2300fc8a9f0af0.png

这里的返回值如果不是空的话,会调用 

e17d8488afea46469abbaba41f42cf62.png

刚好我们的sayHello有返回值所以说就会进去 

a6c4f8fa5af349d698715ee90f86ed16.png umarshaValue里面会进行 反序列化e0f1e67d04d7454395f5ec36be8e2f30.png

 漏洞有两个点,一个是有返回值调用in.readobject一个是处理协议的excutecall(协议叫做JRMP协议) RMI自定的客户端来攻击

 9cca6b3860544a418e1929d32bc40b59.png

客户端请求注册中心(客户端攻击注册中心)

a7762244ed694fb3b8cf63018e1db9d5.png

发现case0 1 2中都有readobject,都是可能被攻击的,是客户端序列化的数据。 

只要创建了远程对象就会生成DGC,DGC的服务端和客户端都可以被攻击。

高版本jdk绕过

jdk8u121左右跟新

注册中心和DGC里面的反序列类做了限制,只要规定的类才能进行反序列化。

 

Registrylmpl::反序列化---->readExternal::LiveRef.read----->LiveRef::stream.saveRef--->Connectioninputstream::incomingRefTable.put赋值--->8bad81f99faa43949ed65c2e8d7af01f.png

然后为了调用,DGCClient.registerRefs-->register b54c9845213e4401a0aca1c16945a4ca.png

31e3576cfb864869bac0ebf53ba8d8ff.png

 

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值