Java安全漫谈 - 06.RMI篇(3)
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.
这是 代码审计知识星球 中Java安全的第六篇文章
上一篇我们详细说了如何利用codebase来加载远程类,在RMI服务端执行任意代码。那么,从原理上来讲,codebase究竟是如何传递进而被利用的呢?
我们曾在第4篇文章抓过RMI的数据包,当时通过数据包简单梳理了RMI通信的组成部分与过程。 这次我们尝试抓取了上一篇文章中攻击RMI的数据包,当然也有2个TCP连接:
-
本机与RMI Registry的通信(在我的数据包中是1099端口)
-
本机与RMI Server的通信(在我的数据包中是64000端口)
我们用
tcp.stream eq 0
来筛选出本机与RMI Registry的数据流:
可见,在与RMI Registry通信的时候Wireshark识别出了协议类型。我们选择其中序号是8的数据包,然后复制Wireshark识别出的
Java Serialization
数据段:
这段数据由0xACED开头,有经验的同学一眼就能看出这是一段Java序列化数据。我们可以使用 SerializationDumper工具( https://github.com/NickstaDB/SerializationDumper ) 对Java序列化数据进行分析:
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的数据流:
可见,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查看这段序列化数据:
可见,我们的
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安全漫谈系列所有文章:
点击“阅读原文”,免费预览知识星球内所有帖子!
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK