During a recent internal penetration test, the need arose to exploit a Java two-stage deserialization vulnerability. This post will walk through how to twist a Nessus plugin, meant to test only for the existence of an RCE vulnerability, into a weaponized exploit that can be utilized to attain a reverse shell on your own attacking server. This was necessary because there were no tools available to easily exploit this vulnerability.
There exists a class of vulnerabilities called Java Deserialization. This is old news for anyone in the field of penetration testing (ysoserial); but, two-stage deserialization is slightly more recent and nuanced. There are many online resources that cover deserialization, so we will not be re-inventing the wheel. We will, however, cover what distinguishes two-stage deserialization from the classics.
While it may seem prerequisite to explain the inner workings of how two-stage deserialization works, it is actually not necessary for the reverse engineering of a Nessus plugin to aid us in our objective. The focus here is on how to weaponize a Nessus plugin.
The vulnerability we’ve exploited was in an Adobe ColdFusion Flex BlazeDS instance. Flex uses AMF (Action Message Format), which is a binary format used to serialize object graphs such as ActionScript objects and XML or send messages between an Adobe Flash client and a remote service, usually a Flash Media Server or third-party alternative. This instance utilized AMF3, which contains new features over AMF0, which contained vulnerabilities that have been exploited and mitigated in the past.
After attaining some background in Java deserialization, the astute hacker will notice that these mitigations often involve the usage of a blacklist against CommonsCollections libraries. Blacklisting in computer security is rarely effective, as the very definition of hacking involves finding creative ways around given restrictions.
What is one more restriction going to do? It will make for a fun Monday.
The Nessus Finding
In a Nessus report, you may find the following vulnerability.
During the preliminary stages of the penetration test, automated tools/scanners are utilized to find any low-hanging fruit, out-of-date software, etc… It is important to note from the image that “Nessus was able to exploit a Java deserialization vulnerability by sending a crafted Java object.” This means that Nessus, as far is it can tell, exploited the vulnerability.
At this point, some research into the vulnerability is due. The finding itself indicates that it is a deserialization vulnerability in the Apache BlazeDS library (http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2017-3066). The finding also points to two articles under “See Also.” The first one, https://codewhitesec.blogspot.com/2017/04/amf.html, proves to be handy. After going through the article and getting an idea of what we might be looking to exploit, we can proceed. What is interesting is the usage of AMF3, the fact that we will probably be using some kind of “JRMPListener” (https://www.tenable.com/security/research/tra-2017-07), and the fact that this technique has already been shown as a deserialization blacklist bypass in 2016 (https://www.tenable.com/security/research/tra-2017-07). Ysoserial was mentioned as well, and it will be handy as well (https://github.com/frohoff/ysoserial).
To focus on the actual Nessus weaponization aspect of this post, I’m going to skim over what I did at the time of this finding, before realizing that I could weaponize the Nessus plugin. Keep in mind that this is a two-stage deserialization attack. The following needs to happen for this to work.
- A listener needs to be set up that will be carrying our secondary payload. This payload will be some sort of Empire one-liner or whatever remote command you’d like to execute.
- The primary payload will be launched, which contains a payload to tell the victim server to call back to our listener and grab the secondary payload. In terms of the actual vulnerability, we’re not quite instructing the victim via actual commands to grab the payload, otherwise we already have RCE. We’re simply suggesting a place externally to grab whatever it needs. If you’ve read the article above, I’m referring to the “Externalizable.readExternal” portion.
Ysoserial contains a relevant tool for this scenario, JRMPListener, which will be handy for the first step outlined above. The second tool, Marshalsec, proves to be a bit clunky to use. There may have been a way to properly utilize the tool, but since Nessus was already exploiting the target, why not leverage what Nessus was using?
The Nessus Payload
After this penetration test, I had new respect for Nessus and its usefulness, outside of simply being a vulnerability scanner. The main reason behind this is NASL. Nessus utilizes Nessus Attack Script Language (NASL) to code plugins that are fired off against targets. These are easily viewable if you have Nessus installed on your system. Let’s look at the ColdFusion AMF Deserialization plugin. This can be found somewhere around “/opt/nessus/lib/nessus/plugins/”, depending on where you installed Nessus. The following is the most interesting part of the script. Note the “amf_payload”.
Now your first instinct may have been, “Ah! So, you just modified this script and changed the cb_address (callback address probably)?” No, because at the top of every plugin, Nessus signs the plugin in a manner that, if the file is changed, Nessus will not use it. Here’s what the signature looks like at the top of the same file.
Well, the exploit itself is quite short, we can just code this in Python. For a sanity check, there was an AMF3 payload generation tool called Marshalsec. I attempted using this tool before diving into Nessus, but for some reason the payload didn’t work. I later realized that the tool was spitting out only part of the whole exploit. This is what the tool was outputting:
Perhaps I was using the tool incorrectly, but I compared the payload with the Nessus payload and noticed some missing parts in the Marshalsec output. The following diagram might help in pointing out these differences.
First, note that the Nessus payload is the longer, more complete payload (middle portion). The Marshalsec payload starts with “0a0733”. This can be found almost a third of the way into the Nessus payload. Secondly, the trailing part of the exploit was completely different between the two. We can tell from the Marshalsec payload that “000004d2” is the equivalent of “mkdword(listening_port)” in the Nessus payload (double word for port 1234). Following that, Marshalsec writes “ffffffff” while Nessus writes “f96a767b”. Since Nessus was able to exploit the target, the choice of which to use is clear.
The Marshalsec payload helps generate the middle portion, while we can grab the “header” and “footer” of the payload from Nessus. The combined bytecode version of the two is the third element in the above figure.
Let’s give this a shot. First, we need two listeners on our attacking system. We need a JRMPListener, armed with an Empire payload. We'll use yososerial for this part. This will serve the second stage of our attack (an Empire launcher) in response to our first stage (what we worked on above). Then we need an Empire listener for our actual Empire agent to call back, of course.
Let’s create a Python script which has our combined Nessus/Marshalsec payload. There is a better way to code for the length of the IP address and the port (convert decimal to hex), but this has been left as a simple exercise for the reader.
After running the primary exploit, JRMPListener receives a connection and responds with the secondary payload:
The Empire listener receives the agent - mission accomplished.
Hopefully this helped demonstrate how Nessus plugins can be used to help code exploits. This should prove to be handy for plugins that demonstrate that remote code execution (RCE) is possible, but available exploits for the vulnerability may be unstable, hard to use, or simply hard to find.