转载

Java安全漫谈 - 06.RMI篇(3)

这是 代码审计知识星球 中Java安全的第六篇文章

上一篇我们详细说了如何利用codebase来加载远程类,在RMI服务端执行任意代码。那么,从原理上来讲,codebase究竟是如何传递进而被利用的呢?

我们曾在第4篇文章抓过RMI的数据包,当时通过数据包简单梳理了RMI通信的组成部分与过程。 这次我们尝试抓取了上一篇文章中攻击RMI的数据包,当然也有2个TCP连接:

  1. 本机与RMI Registry的通信(在我的数据包中是1099端口)

  2. 本机与RMI Server的通信(在我的数据包中是64000端口)

我们用 tcp.stream eq 0 来筛选出本机与RMI Registry的数据流:

Java安全漫谈 - 06.RMI篇(3)

可见,在与RMI Registry通信的时候Wireshark识别出了协议类型。我们选择其中序号是8的数据包,然后复制Wireshark识别出的 Java Serialization 数据段:

Java安全漫谈 - 06.RMI篇(3)

这段数据由0xACED开头,有经验的同学一眼就能看出这是一段Java序列化数据。我们可以使用 SerializationDumper工具( https://github.com/NickstaDB/SerializationDumper 对Java序列化数据进行分析:

Java安全漫谈 - 06.RMI篇(3)

SerializationDumper输出了很多预定义常量,像 TC_BLOCKDATA 这种,它究竟表示什么意思呢?此时我们还得借助Java序列化的协议文档: https://docs.oracle.com/javase/8/docs/platform/serialization/spec/protocol.html

这篇文档里用了一种类似BNF(巴科斯范式)的形式描述了序列化数据的语法,比如我们这里的这段简单的数据,其涉及到如下语法规则:

stream:

magic version contents


contents:

content

contents content


content:

object

blockdata

object:

newObject

newClass

newArray

newString

newEnum

newClassDesc

prevObject

nullReference

exception

TC_RESET


blockdata:

blockdatashort

blockdatalong


blockdatashort:

TC_BLOCKDATA (unsigned byte)<size> (byte)[size]

newString:

TC_STRING newHandle (utf)

TC_LONGSTRING newHandle (long-utf)

其中 TC_BLOCKDATA 这部分对应的是 contents -> content -> blockdata -> blockdatashort TC_STRING 这部分对应的是 contents -> content -> object-> newString 都可以在文档里找到完整的语法定义。

这一整个序列化对象,其实描述的就是一个字符串,其值是 refObj 意思是获取远程的 refObj 对象。

接着我们在序号为10的数据包中获取到了这个对象:

STREAM_MAGIC - 0xac ed

STREAM_VERSION - 0x00 05

Contents

TC_BLOCKDATA - 0x77

Length - 15 - 0x0f

Contents - 0x01a4462ec50000016d8d8d63578008

TC_OBJECT - 0x73

TC_PROXYCLASSDESC - 0x7d

newHandle 0x00 7e 00 00

Interface count - 2 - 0x00 00 00 02

proxyInterfaceNames

0:

Length - 15 - 0x00 0f

Value - java.rmi.Remote - 0x6a6176612e726d692e52656d6f7465

1:

Length - 5 - 0x00 05

Value - ICalc - 0x4943616c63

classAnnotations

TC_NULL - 0x70

TC_ENDBLOCKDATA - 0x78

superClassDesc

TC_CLASSDESC - 0x72

className

Length - 23 - 0x00 17

Value - java.lang.reflect.Proxy - 0x6a6176612e6c616e672e7265666c6563742e50726f7879

serialVersionUID - 0xe1 27 da 20 cc 10 43 cb

newHandle 0x00 7e 00 01

classDescFlags - 0x02 - SC_SERIALIZABLE

fieldCount - 1 - 0x00 01

Fields

0:

Object - L - 0x4c

fieldName

Length - 1 - 0x00 01

Value - h - 0x68

className1

TC_STRING - 0x74

newHandle 0x00 7e 00 02

Length - 37 - 0x00 25

Value - Ljava/lang/reflect/InvocationHandler; - 0x4c6a6176612f6c616e672f7265666c6563742f496e766f636174696f6e48616e646c65723b

classAnnotations

TC_NULL - 0x70

TC_ENDBLOCKDATA - 0x78

superClassDesc

TC_NULL - 0x70

newHandle 0x00 7e 00 03

classdata

java.lang.reflect.Proxy

values

h

(object)

TC_OBJECT - 0x73

TC_CLASSDESC - 0x72

className

Length - 45 - 0x00 2d

Value - java.rmi.server.RemoteObjectInvocationHandler - 0x6a6176612e726d692e7365727665722e52656d6f74654f626a656374496e766f636174696f6e48616e646c6572

serialVersionUID - 0x00 00 00 00 00 00 00 02

newHandle 0x00 7e 00 04

classDescFlags - 0x02 - SC_SERIALIZABLE

fieldCount - 0 - 0x00 00

classAnnotations

TC_NULL - 0x70

TC_ENDBLOCKDATA - 0x78

superClassDesc

TC_CLASSDESC - 0x72

className

Length - 28 - 0x00 1c

Value - java.rmi.server.RemoteObject - 0x6a6176612e726d692e7365727665722e52656d6f74654f626a656374

serialVersionUID - 0xd3 61 b4 91 0c 61 33 1e

newHandle 0x00 7e 00 05

classDescFlags - 0x03 - SC_WRITE_METHOD | SC_SERIALIZABLE

fieldCount - 0 - 0x00 00

classAnnotations

TC_NULL - 0x70

TC_ENDBLOCKDATA - 0x78

superClassDesc

TC_NULL - 0x70

newHandle 0x00 7e 00 06

classdata

java.rmi.server.RemoteObject

values

objectAnnotation

TC_BLOCKDATA - 0x77

Length - 55 - 0x37

Contents - 0x000a556e6963617374526566000e3134302e3233382e33342e3231360000fa00276c0508063e8d45a4462ec50000016d8d8d6357800101

TC_ENDBLOCKDATA - 0x78

java.rmi.server.RemoteObjectInvocationHandler

values

这是一个 java.lang.reflect.Proxy 对象,其中有一段数据储存在 objectAnnotation 中: 0x000a556e6963617374526566000e3134302e3233382e33342e3231360000fa00276c0508063e8d45a4462ec50000016d8d8d6357800101 ,记录了RMI Server的地址和端口。(中间具体调用链,下来后可以自己仔细调试分析)

在拿到RMI Server的地址和端口后,本机就会去连接并正式开始调用远程方法。我们再用 tcp.stream eq 1 筛选出本机与RMI Server的数据流:

Java安全漫谈 - 06.RMI篇(3)

可见,wireshark没有再识别出RMI的协议。我们选择序号为19的数据包,其内容是 50 ac ed 开头,50是指 RMI Call https://github.com/JetBrains/jdk8u_jdk/blob/master/src/share/classes/sun/rmi/transport/TransportConstants.java#L47 ), ac ed 当然是Java序列化数据。

我们使用SerializationDumper查看这段序列化数据:

Java安全漫谈 - 06.RMI篇(3)

可见,我们的 codebase 是通过 [Ljava.rmi.server.ObjID; classAnnotations 传递的。

所以,即使我们没有RMI的客户端,只需要修改 classAnnotations 的值,就能控制codebase,使其指向攻击者的恶意网站。

classAnnotations 是什么?

虽然我们还没讲到Java反序列化,但这里还是补充一下这个知识,否则可能会有的同学一头雾水。

众所周知,在序列化Java对象的时候用到了一个类,叫 ObjectOutputStream 。这个类内部有一个方法 annotateClass ObjectOutputStream 的子类有需要向序列化后的数据里放任何内容,都可以重写这个方法,写入你自己想要写入的数据。然后反序列化时,就可以读取到这个信息并使用。

比如,我们RMI的类 MarshalOutputStream 就将当前的 codebase 写入:

  • https://github.com/JetBrains/jdk8u_jdk/blob/8db9d62a1cfe07fd4260b83ae86e39f80c0a9ff2/src/share/classes/java/rmi/server/RMIClassLoader.java#L657

  • https://github.com/JetBrains/jdk8u_jdk/blob/8db9d62a1c/src/share/classes/sun/rmi/server/LoaderHandler.java#L282

所以,我们在分析序列化数据时看到的 classAnnotations ,实际上就是 annotateClass 方法写入的内容。

END  -

加入『代码审计知识星球』,查看Java安全漫谈系列所有文章:

Java安全漫谈 - 06.RMI篇(3)

点击“阅读原文”,免费预览知识星球内所有帖子!

原文  https://mp.weixin.qq.com/s/wNu73p5cnZJyuGFiiCCxKw
正文到此结束
Loading...