添加链接
link管理
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接
  • 利用JEP290白名单内的类,覆盖掉registerRefs(此时未建立JRMP连接)
  • Registry 处理bind请求,最后会触发DGC逻辑,DGC向registerRefs里保存的Server发送dirty请求
  • DGC底层使用的是StreamRemoteCall.executeCall进行io的传输,executeCall发送完之后,会根据对方的反馈进行不同的操作,当对方返回一定条件时(初步判断是对方RMI Exception),会直接序列化InputStream,造成反序列化漏洞
  • 其中步骤2类似《RMI1》中提到的Client攻击Registry的思路,步骤3与 Registry攻击Client手法类似

    之前一直简单以为是Vuln Registry充当了Vuln Server的角色,走的是Evil Registry Attack Vuln Server的角色,其实并不是。纸上得来终觉浅啊

  • Server发起bind(name,EvilObj)请求
  • Vuln Registry接受到请求,RegistryImpl_Skel处理该请求
  • RegistryImpl_Skel反序列化obj,在JEP290白名单内,功能为触发JRMP连接,连接Evil Registry(对应上图步骤1)(纠正,其实并未建立连接,仅覆盖registerRefs)
  • Vuln Registry底层ConnectionInputStream,负责管理维护连接,有一个表registerRefs,维护当前的client连接记录,因为反序列化EvilObj,被覆盖成了Evil Registry的JRMP连接信息
  • Vuln Registry准备结束掉 Server发起bind请求,调用releaseInputStream(对应上图步骤2),准备通知DGC开始介入监控这个EvilObj的生命周期。
  • Registry DGC初始化监控远程对象时,DGCImpl_Stub会发送一个dirty请求给Server,Server信息保存在registerRefs内。但是此时的Server已经被替换为Evil Registry。
  • DGCImpl_Stub发送dirty请求给Evil Registry,最终是用的是StreamRemoteCall.executeCall来执行(对应步骤3)。《RMI1》中Client攻击Registry时有提到StreamRemoteCall.executeCall的问题,发送完之后会根据对方返回进行下一步操作,case2会直接反序列化InputStream
  • Evil Registry接受到Client 的DGC dirty call,发送反馈payload,被Vuln Registry接受,进入case2逻辑触发反序列化boom💣(对应步骤4)
  • 复现

    在cc链上下断点,调用栈如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    readObject:150, LazyMap (org.apache.commons.collections.map)
    ...
    cc5链
    ...
    executeCall:252, StreamRemoteCall (sun.rmi.transport)
    invoke:375, UnicastRef (sun.rmi.server)
    dirty:109, DGCImpl_Stub (sun.rmi.transport)
    makeDirtyCall:382, DGCClient$EndpointEntry (sun.rmi.transport)
    registerRefs:324, DGCClient$EndpointEntry (sun.rmi.transport)
    registerRefs:160, DGCClient (sun.rmi.transport)
    registerRefs:102, ConnectionInputStream (sun.rmi.transport)
    releaseInputStream:157, StreamRemoteCall (sun.rmi.transport)
    dispatch:80, RegistryImpl_Skel (sun.rmi.registry)
    oldDispatch:468, UnicastServerRef (sun.rmi.server)
    dispatch:300, UnicastServerRef (sun.rmi.server)
    run:200, Transport$1 (sun.rmi.transport)
    run:197, Transport$1 (sun.rmi.transport)
    doPrivileged:-1, AccessController (java.security)
    serviceCall:196, Transport (sun.rmi.transport)
    handleMessages:573, TCPTransport (sun.rmi.transport.tcp)
    run0:835, TCPTransport$ConnectionHandler (sun.rmi.transport.tcp)
    lambda$run$0:688, TCPTransport$ConnectionHandler (sun.rmi.transport.tcp)
    run:-1, 1166320987 (sun.rmi.transport.tcp.TCPTransport$ConnectionHandler$$Lambda$5)
    doPrivileged:-1, AccessController (java.security)
    run:687, TCPTransport$ConnectionHandler (sun.rmi.transport.tcp)
    runWorker:1149, ThreadPoolExecutor (java.util.concurrent)
    run:624, ThreadPoolExecutor$Worker (java.util.concurrent)
    run:748, Thread (java.lang)

    需要跟一遍流程,这里就不详细的讲解了,重点需要关注

  • bind流程,如何触发DGC,如何发送Dirty请求

  • sun.rmi.transport.StreamRemoteCall.in.incomingRefTable是如何被覆盖的

    正常bind 的交互流程

    以HelloServer、Registry为例,一次正常的bind产生的网络交互行为:
    upload successful

    1. Server端逻辑

    server 序列化 “hello”、new HelloImpl(),发送给registry 1088端口,并监听等待DGC call

    对应的代码逻辑
    upload successful

    跟进invoke(var3),到StreamRemoteCall.executeCall。发送完数据之后会监听返回,并判断,如果第一个bypte为2则进行序列化(初步判断==2表示registry返回异常)

    这里的 var14 = this.in.readObject(); 就是多次提到的《RMI1》中registry攻击client的原理

    2. Registry逻辑

    registry 收到server bind请求包,反序列化”hello”、HelloImpl,并准备释放链接,释放链接前会将这些这些远程类引用记录传给DGC逻辑,DGC建立每个远程类的管理记录,且发送一个dirty请求给server

    注意这里的DGC逻辑

    对应的几个重要代码逻辑
    upload successful
    upload successful
    upload successful

    覆盖StreamRemoteCall.in.incomingRefTable

    先来看一遍反序列化EvilObj之后的对比:可以看到incomingRefTable已经被替换为恶意的Evil Registry连接地址了

    incomingRefTable会被当作DGC发送dirty时,认为的server地址

    具体是如何被覆盖的?

    断下ConnectionInputStream.saveRef

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    saveRef:73, ConnectionInputStream (sun.rmi.transport)
    read:305, LiveRef (sun.rmi.transport)
    readExternal:489, UnicastRef (sun.rmi.server)
    readObject:455, RemoteObject (java.rmi.server)
    invoke0:-1, NativeMethodAccessorImpl (sun.reflect)
    invoke:62, NativeMethodAccessorImpl (sun.reflect)
    invoke:43, DelegatingMethodAccessorImpl (sun.reflect)
    invoke:498, Method (java.lang.reflect)
    invokeReadObject:1158, ObjectStreamClass (java.io)
    readSerialData:2176, ObjectInputStream (java.io)
    readOrdinaryObject:2067, ObjectInputStream (java.io)
    readObject0:1571, ObjectInputStream (java.io)
    defaultReadFields:2285, ObjectInputStream (java.io)
    readSerialData:2209, ObjectInputStream (java.io)
    readOrdinaryObject:2067, ObjectInputStream (java.io)
    readObject0:1571, ObjectInputStream (java.io)
    readObject:431, ObjectInputStream (java.io)
    dispatch:76, RegistryImpl_Skel (sun.rmi.registry)
  • 实例{ConnectionInputStream@1023}.readObject()
    upload successful

  • POC中的 LiveRef(id, te, false)反序列化

    LiveRef.read(var0={ConnectionInputStream@1023})
    upload successful

  • 继续调用{ConnectionInputStream@1023}.saveRef, incomingRefTable被更新
    upload successful

    RemoteObjectInvocationHandler

    如何将UnicastRef在Register上反序列化,bind参数为Remote类型,不能直接传UnicastRef。

    参考【2】,给出了寻找RemoteObjectInvocationHandler 的过程,利用Proxy 和Handler 动态转换,是很典型的反序列化利用手段。原文讲的很详细了,而且发现了其他几个 衔接类,具体参考原文。

    小结

    本节详细跟踪了JEP290的绕过流程,发现并不是简单的JRMP连接+Registry攻击Server的原理,而是因为DGC机制、StreamRemoteCall、incomingRefTable一些更为复杂的逻辑。

    发现者需要对RMI、DGC的逻辑有深刻的了解,很是赞叹与崇拜。与其相比,发现RemoteObjectInvocationHandler都只是反序列化利用基础知识而已。

    参考【1】中提到还有8u231之后还有修复及绕过,留待下篇再学习吧

  • [1] 针对RMI服务的九重攻击 - 下
  • [2] 一次攻击内网rmi服务的深思
  • 缺失模块。
    1、请确保node版本大于6.2
    2、在博客根目录(注意不是yilia根目录)执行以下命令:
    npm i hexo-generator-json-content --save

    3、在根目录_config.yml里添加配置: jsonContent: meta: false pages: false posts: title: true date: true path: true text: false raw: false content: false slug: false updated: false comments: false link: false permalink: false excerpt: false categories: false tags: true