Leveraging Jython to Bypass XStream Protections Against RCE

By Devin Dwyer –

XStream is a third party library that supports the unmarshalling of XML to Java objects. Unmarshalling, similar to deserialization, is the conversion from formatted XML to Java objects. Below is an illustration of how a Rectangle object (assuming the application has access to its class definition) can be derived from XML using XStream:

d1.png

Although unmarshalling XML to objects may be convenient, a major security vulnerability arises when XML data received by untrusted users is processed by XStream. In December of 2013, a vulnerability was found in XStream that, when the library’s unmarshaller was fed with specially crafted XML, resulted in an RCE. An awesome write-up on the vulnerability can be found here.

The root cause of this vulnerability is XStream’s support for dynamic proxies. As a result of this publicized vulnerability, XStream decided to blacklist the unmarshalling of the EventHandler class which was the proxy class used in the exploit to execute arbitrary commands.

Fast forward to a few months ago when I decided to go through a number of classes that could also be used as a malicious proxy (in replacement of EventHandler.class).

The XML that XStream uses to specify the initialization of a dynamic proxy is the following:

 

someInterface

<handler class=”someClass”>

… declare object members….

 

 

When a method is called on an object that implements someInterface, the invoke() method is called within the specified handler class (someClass). The handler class must implement the InvocationHandler interface provided by the Java library. It requires the implementation of the invoke() method.

The original exploit that was found in 2013 specified the EventHandler class to execute arbitrary commands. By providing EventHandler as the handler class and declaring malicious members of it’s instantiation, an attacker was capable of running commands on the server. XStream fixed the vulnerability by disallowing the unmarshalling of the EventHandler class specifically. This, however, does not stop the abuse of the dynamic proxy feature as long as there is another handler class’s invoke() method that can be used to perform a malicious action.

The goal was to find a way to use another handler class to perform something malicious.

After reviewing the invoke() method in several classes, the first class that I could find that was a perceived danger is the PyFunction class provided by the Jython library.

PyFunction

The PyFunction class implements InvocationHandler and has the following invoke() method:

public Object invoke( Object proxy, Method method, Object[] args ) throws Throwable {

// Handle invocation when invoked through Proxy (as coerced to single method interface)

if (method.getDeclaringClass() == Object.class) {

return method.invoke( this, args );

} else if (args == null || args.length == 0) {

return __call__().__tojava__(method.getReturnType());

} else {

return __call__(Py.javas2pys(args)).__tojava__(method.getReturnType());

}

}

So when this handler class gets invoked, the __call__ method of the PyFunction class is called. The __call__() method eventually executes the python function.

The PyFunction class also has the following members which we can manipulate when declaring it in XML format:

public String __name__;

public PyObject __doc__;

public PyObject func_globals;

public PyObject[] func_defaults;

public PyCode func_code;

public PyObject __dict__;

public PyObject func_closure;

public PyObject __module__;

The significant member of the PyFunction class is the func_code object. This object contains the python function that is executed by the __call__() method. So if we can declare that func_code object with our malicious python commands, they will be called by the PyFunction’s invoke() method.

That is where we can use the PyBytecode class.

PyBytecode

Usually, when using the Jython library, the following is the standard way of grabbing a PyFunction object from a python file that contains java code:

PythonInterpreter interpreter = new PythonInterpreter();

interpreter.execfile(“myJythonFile.py”);

PyFunction pyFuntion = (PyFunction) interpreter.get(“myJythonFunction”, PyFunction.class);

pyFuntion.__call__();

Ideally, we could just marshall the pyFunction object using XStream.toXML and then throw that into the tag in our payload. Unfortunately, the PyCode object that contains the executable code is of type PyTableCode. This derivation of the PyCode class contains a reference to the python file and uses it to run the code with the Jython interpreter. This means that we are unable to use this PyCode derivation for serializing because when we unmarshall the XML, our JVM will have no clue what that reference is.

There is, however, another derivation of the PyCode class that we can use. It is called PyBytecode. Unlike PyTableCode, PyBytecode actually stores the python bytecode to be executed in a string. These are the members of the PyByteCode:

debug = defaultDebug;

co_argcount = nargs = argcount;

co_varnames = varnames;

co_nlocals = nlocals; // maybe assert = varnames.length;

co_filename = filename;

co_firstlineno = firstlineno;

co_cellvars = cellvars;

co_freevars = freevars;

co_name = name;

co_flags = new CompilerFlags(flags);

varargs = co_flags.isFlagSet(CodeFlag.CO_VARARGS);

varkwargs = co_flags.isFlagSet(CodeFlag.CO_VARKEYWORDS);

co_stacksize = stacksize;

co_consts = constants;

co_names = names;

co_code = getBytes(codestring);

co_lnotab = getBytes(lnotab);

As you can see, these attributes are specific to the python bytecode to be executed. The co_code is the actual string of bytes that will be executed by the PyBytecode class when the __call__() method is called by the PyFunction handler class. This is much more useful because there is no reference to the instructions to be executed, it is actually stored in the PyCode object which we can define in our XML.

Creating A POC

Considering we need a string of python bytecode and all of those attributes to accompany it, we have to find a way to view them from a python function that we already created.

Given the following function:

def test():

print(“hello!”) 

We can create get the bytecode information by printing the values from our python test object:

test.__code__.co_argcount

test.__code__.co_code

test.__code__.consts

test.__code__.co_lnotab

We can use these values to create the PyBytecode object:

PyBytecode pb = new PyBytecode(argcount, nlocals, stacksize, flags, codestring, constants, names, varnames, filename, name, firstlineno, lnotab);

And then create a PyFunction object using the PyBytecode object:

PyFunction func2 = new PyFunction(func_global, null, pb, null, null);

And then finally use XStream.toXML to marshall it.

From here, we can use the XML that the XStream.toXML method returns and plant it in the original XStream exploit in place of the EventHandler class.

When the XML is unmarshalled by the XStream library, the python byte code get executed immediately, resulting an RCE. Unfortunately, by default, the Jython interpreter does not have access to the standard Python library. Therefore, one could find a way to import it directly (guessing at the path) or upload a zipped version of it to a known directory and use zipimport. Without the standard library, all one can do is read/write/open files (which is still pretty significant and actually poses an interesting thought experiment: what are malicious actions you can do with Python when all you can do is read/write/open files?).

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s