25

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

 4 years ago
source link: https://www.tuicool.com/articles/vYbMvae
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

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

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

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

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

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

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

IVjE7bB.jpg!web

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

nE7V7vv.jpg!web

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

NBBRBjI.png!web

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的数据流:

Zjqimui.jpg!web

可见,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查看这段序列化数据:

3M7jYb7.jpg!web

可见,我们的 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安全漫谈系列所有文章:

6vyIVb2.jpg!web

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


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK