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

  • A+
所属分类:代码审计

这是代码审计知识星球中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 -> blockdatashortTC_STRING这部分对应的是contents -> content -> object-> newString都可以在文档里找到完整的语法定义。

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

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

STREAM_MAGIC - 0xac edSTREAM_VERSION - 0x00 05Contents  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 Callhttps://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。这个类内部有一个方法annotateClassObjectOutputStream的子类有需要向序列化后的数据里放任何内容,都可以重写这个方法,写入你自己想要写入的数据。然后反序列化时,就可以读取到这个信息并使用。

比如,我们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安全漫谈 - 06.RMI篇(3)

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

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

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

本文始发于微信公众号(代码审计):Java安全漫谈 - 06.RMI篇(3)

发表评论

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: