JAVA反序列化RMI专题1

Java反序列化RMI专题

1.创建远程服务

用exportObject()指定到发布的 IP 与端口,端口的话是一个随机值。至始至终复杂的地方其实都是在赋值,创建类,进行各种各样的封装,实际上并不复杂。是Stub

创建一个临时的对象,把临时封装的对象put到一个hash表中

2.创建注册中心+绑定

总结一下比较简单,注册中心这里其实和发布远程对象很类似,不过多了一个持久的对象,这个持久的对象就成为了注册中心。

创建一个永久对象,把永久封装的对象put到一个表中。是skel

绑定的话就更简单了,一句话形容一下就是 hashTable.put(IP, port)

3.客户端请求,客户端调用注册中心

查找远程对象,首先获取注册中心。然后在lookup里面把对象给反序列化掉了,然后invoke()—>call.executeCall()—>out.getDGCAckHandler() 处理真实的网络请求。out.getDGCAckHandler()在这里面有个try,这里它有一个异常存在潜在攻击的可能性

不难理解,in 就是数据流里面的东西。这里获取异常的本意应该是在报错的时候把一整个信息都拿出来,这样会更清晰一点,但是这里就出问题了 ———— 如果一个注册中心返回一个恶意的对象,客户端进行反序列化,这就会导致漏洞。这里的漏洞相比于其他漏洞更为隐蔽。

4. 客户端请求,客户端请求服务端

先说说存在攻击的点吧,在注册中心 –> 服务端这里,查找远程对象的时候是存在攻击的。具体表现形式是服务端打客户端,入口类在call.executeCall(),里面抛出异常的时候会进行反序列化。
这里可以利用 URLClassLoader 来打,具体的攻击在后续文章会写。

在服务端 —> 客户端这里,也是存在攻击的,一共是两个点:一个是 call.executeCall(),另一个点是 unmarshalValueSee 这里。

5. 客户端发起请求,注册中心如何处理.

我们与注册中心进行交互可以使用如下几种方式:

  • list
  • bind
  • rebind
  • unbind
  • lookup

这几种方法位于 RegistryImpl_Skel#dispatch 中,也就是我们现在 dispatch 这个方法的地方。

如果存在对传入的对象调用 readObject 方法,则可以利用,dispatch 里面对应关系如下:

  • 0->bind
  • 1->list
  • 2->lookup
  • 3->rebind
  • 4->unbind

只要中间是有反序列化就是可以攻击的,而且我们是从客户端打到注册中心,这其实是黑客们最喜欢的攻击方式。

除了list都可以攻击

简单,注册中心就是处理 Target,进行 Skel 的生成与处理。

漏洞点是在 dispatch 这里,存在反序列化的入口类。这里可以结合 CC 链子打的。

DGCImpl_Stub 这个类下,它有两个方法,一个是 clean,另外一个是 dirty。clean 就是”强”清除内存,dirty 就是”弱”清除内存。

同样在 DGCImpl_Skel 这个类下也存在反序列化的漏洞,如图。

  • 是自动创建的一个过程,用于清理内存。

漏洞点在客户端与服务端都存在,存在于 SkelStub 当中。这也就是所谓的 JRMP 绕过

总结

  • 如果是漏洞利用的话,单纯攻击 RMI 意义是不大的,不论是 codespace 的那种利用,难度很高,还是说三者互相打这种,意义都不是很大,因为在 jdk8u121 之后都基本修复完毕了。

RMI 多数的利用还是在后续的 fastjson,strust2 这种类型的攻击组合拳比较多,希望这篇文章能对正在学习 RMI 的师傅们提供一点帮助。

RMI 底层通讯采用了Stub (运行在客户端) 和 Skeleton (运行在服务端) 机制,RMI 调用远程方法的大致如下:

  1. RMI 客户端在调用远程方法时会先创建 Stub ( sun.rmi.registry.RegistryImpl_Stub )。
  2. Stub 会将 Remote 对象传递给远程引用层 ( java.rmi.server.RemoteRef ) 并创建 java.rmi.server.RemoteCall( 远程调用 )对象。
  3. RemoteCall 序列化 RMI 服务名称、Remote 对象。
  4. RMI 客户端的远程引用层传输 RemoteCall 序列化后的请求信息通过 Socket 连接的方式传输到 RMI 服务端的远程引用层。
  5. RMI服务端的远程引用层( sun.rmi.server.UnicastServerRef )收到请求会请求传递给 Skeleton ( sun.rmi.registry.RegistryImpl_Skel#dispatch )。
  6. Skeleton 调用 RemoteCall 反序列化 RMI 客户端传过来的序列化。
  7. Skeleton 处理客户端请求:bind、list、lookup、rebind、unbind,如果是 lookup 则查找 RMI 服务名绑定的接口对象,序列化该对象并通过 RemoteCall 传输到客户端。
  8. RMI 客户端反序列化服务端结果,获取远程对象的引用。
  9. RMI 客户端调用远程方法,RMI服务端反射调用RMI服务实现类的对应方法并序列化执行结果返回给客户端。
  10. RMI 客户端反序列化 RMI 远程方法调用结果。

参考:

https://drun1baby.top/2022/07/19/Java%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E4%B9%8BRMI%E4%B8%93%E9%A2%9801-RMI%E5%9F%BA%E7%A1%80/#3-%E5%AE%A2%E6%88%B7%E7%AB%AF%E8%AF%B7%E6%B1%82%EF%BC%8C%E5%AE%A2%E6%88%B7%E7%AB%AF%E8%B0%83%E7%94%A8%E6%B3%A8%E5%86%8C%E4%B8%AD%E5%BF%83