RMI2
中成功实现了codebase进行加载恶意类,但是在实际环境确实难实现符合环境,所以只作为一个了解和认识。
这篇文章主要分析codebase是如何在RMI中进行传递的,以及对分析Java序列化数据的工具SerializationDumper
的使用介绍
Java安全[RMI(3)]
数据包解析以及SerializationDumper工具
这里选择的是JRMI,Call
的数据包,打开右键Java Serialization
数据段,复制Hex Stream
,看到ac ed
就知道后面是java序列化数据
我们可以用
tcp.stream eq 0
来筛选出本机与RMI Registry的数据流
tcp.stream eq 1
来筛选出本机与RMI Server的数据流
将复制的Hex Stream
用工具解析
但是有很多参数看不懂,可以看看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) (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
对 象。
JRMI,ReturnData
数据包大一些,下面用它来展示
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 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89
| STREAM_MAGIC - 0xac ed STREAM_VERSION - 0x00 05 Contents TC_BLOCKDATA - 0x77 Length - 15 - 0x0f Contents - 0x0120dea1410000018b3e3ad3308005 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 - 11 - 0x00 0b Value - RMI_2.ICalc - 0x524d495f322e4943616c63 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 - 56 - 0x38 Contents - 0x000a556e6963617374526566000f3139322e3136382e3136392e3134360000c5b2e3476369ea6a56e120dea1410000018b3e3ad330800101 TC_ENDBLOCKDATA - 0x78 java.rmi.server.RemoteObjectInvocationHandler values <Dynamic Proxy Class>
|
这是个java.lang.reflect.Proxy
对象,其中有段数据存储在objectAnnotation
中:0x000a556e6963617374526566000f3139322e3136382e3136392e3134360000c5b2e3476369ea6a56e120dea1410000018b3e3ad330800101
这段数据记录了RMI Server的地址和端口。
然后再看看,本机与RMI Server的交互,虽然wireshark没有识别出RMI,但是从数据包中可以看到50 ac ed
,所以是存在序列化数据,一样用工具解析试试,只保留ac ed
开头。
可以看到codebase
是通过过 java.rmi.server.Obj
;的 classAnnotations
传递的。
= >即使我们没有RMI的客户端,只需要修改 classAnnotations
的值,就能控制codebase
,使其 指向攻击者的恶意网站。
classAnnotations是什么?
在序列化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 方法写入的 内容。