Projects
home:zhangxiang:branches:Mega:23.03
xstream
_service:tar_scm:CVE-2021-43859.patch
Sign Up
Log In
Username
Password
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
File _service:tar_scm:CVE-2021-43859.patch of Package xstream
From e8e88621ba1c85ac3b8620337dd672e0c0c3a846 Mon Sep 17 00:00:00 2001 From: joehni <joerg.schaible@gmx.de> Date: Mon, 27 Dec 2021 01:24:08 +0100 Subject: [PATCH] Describe and fix CVE-2021-43859. --- .../src/content/CVE-2021-43859.html | 199 ++++++++++++++++++ .../src/content/security.html | 40 +++- xstream-distribution/src/content/website.xml | 1 + .../com/thoughtworks/xstream/XStream.java | 42 +++- .../collections/CollectionConverter.java | 6 +- .../converters/collections/MapConverter.java | 6 +- .../extended/NamedMapConverter.java | 5 +- .../xstream/core/SecurityUtils.java | 56 +++++ .../xstream/core/TreeUnmarshaller.java | 3 + .../security/AbstractSecurityException.java | 29 +++ .../security/ForbiddenClassException.java | 6 +- .../security/InputManipulationException.java | 27 +++ .../acceptance/SecurityVulnerabilityTest.java | 155 +++++++++++++- 14 files changed, 568 insertions(+), 21 deletions(-) create mode 100644 xstream-distribution/src/content/CVE-2021-43859.html create mode 100644 xstream/src/java/com/thoughtworks/xstream/core/SecurityUtils.java create mode 100644 xstream/src/java/com/thoughtworks/xstream/security/AbstractSecurityException.java create mode 100644 xstream/src/java/com/thoughtworks/xstream/security/InputManipulationException.java diff --git a/xstream-distribution/src/content/CVE-2021-43859.html b/xstream-distribution/src/content/CVE-2021-43859.html new file mode 100644 index 00000000..531298d3 --- /dev/null +++ b/xstream-distribution/src/content/CVE-2021-43859.html @@ -0,0 +1,199 @@ +<html> +<!-- + Copyright (C) 2021 XStream committers. + All rights reserved. + + The software in this package is published under the terms of the BSD + style license a copy of which has been included with this distribution in + the LICENSE.txt file. + + Created on 23. December 2021 by Joerg Schaible + --> + <head> + <title>CVE-2021-43859</title> + </head> + <body> + + <h2 id="vulnerability">Vulnerability</h2> + + <p>CVE-2021-43859: XStream can cause a Denial of Service by injecting highly recursive collections or maps.</p> + + <h2 id="affected_versions">Affected Versions</h2> + + <p>All versions until and including version 1.4.18 are affected.</p> + + <h2 id="description">Description</h2> + + <p>The processed stream at unmarshalling time contains type information to recreate the formerly written objects. + XStream creates therefore new instances based on these type information. An attacker can manipulate the processed + input stream and replace or inject objects, that result in exponential recursively hashcode calculation, causing a denial + of service.</p> + + <h2 id="reproduction">Steps to Reproduce</h2> + + <p>The attack uses the hashcode implementation of collection types in the Java runtime. Following types are affected with + lastest Java versions available in December 2021:</p> + <ul> + <li>java.util.HashMap</li> + <li>java.util.HashSet</li> + <li>java.util.Hashtable</li> + <li>java.util.LinkedHashMap</li> + <li>java.util.LinkedHashSet</li> + <li>java.util.Stack (older Java revisions only)</li> + <li>java.util.Vector (older Java revisions only)</li> + <li>Other third party collection implementations that use their element's hash code may also be affected</li> + </ul> + <p>Create a simple HashSet and use XStream to marshal it to XML. Replace the XML with following snippet, increase the + depth of the structure and unmarshal it with XStream:</p> +<div class="Source XML"><pre><set> + <set> + <string>a</string> + <set> + <string>a</string> + <set> + <string>a</string> + </set> + <set> + <string>b</string> + </set> + </set> + <set> + <set reference="../../set/set"/> + <string>b</string> + <set reference="../../set/set[2]"/> + </set> + </set> + <set> + <set reference="../../set/set"/> + <string>b</string> + <set reference="../../set/set[2]"/> + </set> +</set> +</pre></div> +<div class="Source Java"><pre>XStream xstream = new XStream(); +xstream.fromXML(xml); +</pre></div> + <p>Create a simple HashMap and use XStream to marshal it to XML. Replace the XML with following snippet, increase the + depth of the structure and unmarshal it with XStream:</p> +<div class="Source XML"><pre><map> + <entry> + <map> + <entry> + <string>a</string> + <string>b</string> + </entry> + <entry> + <map> + <entry> + <string>a</string> + <string>b</string> + </entry> + <entry> + <map> + <entry> + <string>a</string> + <string>b</string> + </entry> + </map> + <map> + <entry> + <string>c</string> + <string>d</string> + </entry> + </map> + </entry> + <entry> + <map reference="../../entry[2]/map[2]"/> + <map reference="../../entry[2]/map"/> + </entry> + </map> + <map> + <entry> + <string>c</string> + <string>d</string> + </entry> + <entry> + <map reference="../../../entry[2]/map"/> + <map reference="../../../entry[2]/map[2]"/> + </entry> + <entry> + <map reference="../../../entry[2]/map[2]"/> + <map reference="../../../entry[2]/map"/> + </entry> + </map> + </entry> + <entry> + <map reference="../../entry[2]/map[2]"/> + <map reference="../../entry[2]/map"/> + </entry> + </map> + <map> + <entry> + <string>c</string> + <string>d</string> + </entry> + <entry> + <map reference="../../../entry[2]/map"/> + <map reference="../../../entry[2]/map[2]"/> + </entry> + <entry> + <map reference="../../../entry[2]/map[2]"/> + <map reference="../../../entry[2]/map"/> + </entry> + </map> + </entry> + <entry> + <map reference="../../entry[2]/map[2]"/> + <map reference="../../entry[2]/map"/> + </entry> +</map> +</pre></div> +<div class="Source Java"><pre>XStream xstream = new XStream(); +xstream.fromXML(xml); +</pre></div> + + <p>As soon as the XML is unmarshalled, the hash codes of the elements are calculated and the calculation time increases + exponentially due to the highly recursive structure.</p> + + <p>Note, this example uses XML, but the attack can be performed for any supported format, that supports references, i.e. + JSON is not affected.</p> + + <h2 id="impact">Impact</h2> + + <p>The vulnerability may allow a remote attacker to allocate 100% CPU time on the target system depending on CPU + type or parallel execution of such a payload resulting in a denial of service only by manipulating the processed + input stream.</p> + + <h2 id="workarounds">Workarounds</h2> + + <p>If your object graph does not use referenced elements at all, you may simply set the NO_REFERENCE mode:</p> + +<div class="Source Java"><pre>XStream xstream = new XStream(); +xstream.setMode(XStream.NO_REFERENCES); +</pre></div> + + <p>If your object graph contains neither a Hashtable, HashMap nor a HashSet (or one of the linked variants of it) then you + can use the security framework to deny the usage of these types:</p> + +<div class="Source Java"><pre>XStream xstream = new XStream(); +xstream.denyTypes(new Class[]{ + java.util.HashMap.class, java.util.HashSet.class, java.util.Hashtable.class, java.util.LinkedHashMap.class, java.util.LinkedHashSet.class +}); +</pre></div> + + <p>Unfortunately these types are very common. If you only use HashMap or HashSet and your XML refers these only as default + map or set, you may additionally change the default implementation of java.util.Map and java.util.Set at unmarshalling time:</p> + +<div class="Source Java"><pre>xstream.addDefaultImplementation(java.util.TreeMap.class, java.util.Map.class); +xstream.addDefaultImplementation(java.util.TreeSet.class, java.util.Set.class); +</pre></div> + + <p>However, this implies that your application does not care about the implementation of the map and all elements are comparable.</p> + + <h2 id="credits">Credits</h2> + + <p>r00t4dm at Cloud-Penetrating Arrow Lab found and reported the issue to XStream and provided the required information to + reproduce it.</p> + + </body> + </html> diff --git a/xstream-distribution/src/content/security.html b/xstream-distribution/src/content/security.html index f0e0177c..fe0a8217 100644 --- a/xstream-distribution/src/content/security.html +++ b/xstream-distribution/src/content/security.html @@ -30,13 +30,13 @@ context of the server running the XStream process or cause a denial of service by crashing the application or manage to enter an endless loop consuming 100% of CPU cycles.</p> - <p class=highlight>Note: XStream supports other data formats than XML, e.g. JSON. Those formats can be used for - the same attacks.</p> + <p class=highlight>Note: XStream supports other data formats than XML, e.g. JSON. Those formats can usually be used + for the same attacks.</p> - <p>Note, that the XML data can be manipulated on different levels. For example, manipulating values on existing - objects (such as a price value), accessing private data, or breaking the format and causing the XML parser to fail. - The latter case will raise an exception, but the former case must be handled by validity checks in any application - which processes user-supplied XML.</p> + <p>The XML data can be manipulated on different levels. For example, manipulating values on existing objects (such + as a price value), accessing private data, or breaking the format and causing the XML parser to fail. The latter + case will raise an exception, but the former case must be handled by validity checks in any application which + processes user-supplied XML.</p> <h2 id="CVEs">Documented Vulnerabilities</h2> @@ -49,6 +49,14 @@ <h2 id="CVEs">Documented Vulnerabilities</h2> <th>CVE</th> <th>Description</th> </tr> + <tr> + <th>Version 1.4.18</th> + <td></td> + </tr> + <tr> + <th><a href="CVE-2021-43859.html">CVE-2021-43859</a></th> + <td>XStream can cause a Denial of Service by injecting highly recursive collections or maps.</td> + </tr> <tr> <th>Version 1.4.17</th> <td></td> @@ -258,6 +266,16 @@ <h2 id="implicit">Implicit Security</h2> because no-one can assure, that no other vulnerability is found. A better approach is the usage of a whitelist i.e. the allowed class types are setup explicitly. This is the default for XStream 1.4.18 (see below).</p> + <p>XStream supports references to objects already occuring on the object graph in an earlier location. This allows + an attacker to create a highly recursive object structure. Some collections or maps calculate the position of a + member based on the data of the member itself. This is true for sorting collections or maps, but also for + collections or maps based on the hash code of the individual members. The calculation time for the member's + position can increase exponentially depending on the recursive depth of the structure and cause therefore a Denial + of Service. Therefore XStream measures the time consumed to add an element to a collection or map since version + 1.4.19. Normally this operation is performed in a view milliseconds, but if adding elements take longer than a + second, then the time is accumulated and an exception is thrown if it exceeds a definable limit (20 seconds by + default).</p> + <h2 id="explicit">Explicit Security</h2> <p>Starting with XStream 1.4.7, it is possible to define <a href="#framework">permissions</a> for types, to check @@ -285,6 +303,16 @@ <h2 id="explicit">Explicit Security</h2> <p class=highlight>Apart from value manipulations, this implementation still allows the injection of allowed objects at wrong locations, e.g. inserting an integer into a list of strings.</p> + <p>To avoid an attack based on the position of an element in a collection or map, you should also use XStream's + default converters for 3rd party or own implementations of collections or maps. Own custom converters of such + types should measure the time to add an element at deserialization time using the following sequence in the + implementation of the unmarshal method:<div class="Source Java"> +<pre>// unmarshal element of collection +long now = System.currentTimeMillis(); +// add element here, e.g. list.add(element); +SecurityUtils.checkForCollectionDoSAttack(context, now); +</pre></div></p> + <h2 id="validation">XML Validation</h2> <p>XML itself supports input validation using a schema and a validating parser. With XStream, you can use e.g. a diff --git a/xstream-distribution/src/content/website.xml b/xstream-distribution/src/content/website.xml index 157baeb9..ad85d03d 100644 --- a/xstream-distribution/src/content/website.xml +++ b/xstream-distribution/src/content/website.xml @@ -89,6 +89,7 @@ <page>CVE-2021-39152.html</page> <page>CVE-2021-39153.html</page> <page>CVE-2021-39154.html</page> + <page>CVE-2021-43859.html</page> <page>CVE-2020-26217.html</page> <page>CVE-2020-26258.html</page> <page>CVE-2020-26259.html</page> diff --git a/xstream/src/java/com/thoughtworks/xstream/XStream.java b/xstream/src/java/com/thoughtworks/xstream/XStream.java index 7d90dc7f..9787059d 100644 --- a/xstream/src/java/com/thoughtworks/xstream/XStream.java +++ b/xstream/src/java/com/thoughtworks/xstream/XStream.java @@ -151,6 +151,7 @@ import com.thoughtworks.xstream.mapper.XStream11XmlFriendlyMapper; import com.thoughtworks.xstream.security.AnyTypePermission; import com.thoughtworks.xstream.security.ArrayTypePermission; +import com.thoughtworks.xstream.security.InputManipulationException; import com.thoughtworks.xstream.security.ExplicitTypePermission; import com.thoughtworks.xstream.security.InterfaceTypePermission; import com.thoughtworks.xstream.security.NoPermission; @@ -295,6 +296,8 @@ // CAUTION: The sequence of the fields is intentional for an optimal XML output of a // self-serialization! + private int collectionUpdateLimit = 20; + private ReflectionProvider reflectionProvider; private HierarchicalStreamDriver hierarchicalStreamDriver; private ClassLoaderReference classLoaderReference; @@ -329,6 +332,9 @@ public static final int PRIORITY_LOW = -10; public static final int PRIORITY_VERY_LOW = -20; + public static final String COLLECTION_UPDATE_LIMIT = "XStreamCollectionUpdateLimit"; + public static final String COLLECTION_UPDATE_SECONDS = "XStreamCollectionUpdateSeconds"; + private static final String ANNOTATION_MAPPER_TYPE = "com.thoughtworks.xstream.mapper.AnnotationMapper"; private static final Pattern IGNORE_ALL = Pattern.compile(".*"); @@ -1182,6 +1188,23 @@ public void setMarshallingStrategy(MarshallingStrategy marshallingStrategy) { this.marshallingStrategy = marshallingStrategy; } + /** + * Set time limit for adding elements to collections or maps. + * + * Manipulated content may be used to create recursive hash code calculations or sort operations. An + * {@link InputManipulationException} is thrown, it the summed up time to add elements to collections or maps + * exceeds the provided limit. + * + * Note, that the time to add an individual element is calculated in seconds, not milliseconds. However, attacks + * typically use objects with exponential growing calculation times. + * + * @param maxSeconds limit in seconds or 0 to disable check + * @since upcoming + */ + public void setCollectionUpdateLimit(int maxSeconds) { + collectionUpdateLimit = maxSeconds; + } + /** * Serialize an object to a pretty-printed XML String. * @@ -1388,6 +1411,13 @@ public Object unmarshal(HierarchicalStreamReader reader, Object root) { */ public Object unmarshal(HierarchicalStreamReader reader, Object root, DataHolder dataHolder) { try { + if (collectionUpdateLimit >= 0) { + if (dataHolder == null) { + dataHolder = new MapBackedDataHolder(); + } + dataHolder.put(COLLECTION_UPDATE_LIMIT, new Integer(collectionUpdateLimit)); + dataHolder.put(COLLECTION_UPDATE_SECONDS, new Integer(0)); + } return marshallingStrategy.unmarshal(root, reader, dataHolder, converterLookup, mapper); } catch (ConversionException e) { Package pkg = getClass().getPackage(); @@ -2053,15 +2083,23 @@ public ObjectInputStream createObjectInputStream(final HierarchicalStreamReader * @see #createObjectInputStream(com.thoughtworks.xstream.io.HierarchicalStreamReader) * @since 1.4.10 */ - public ObjectInputStream createObjectInputStream(final HierarchicalStreamReader reader, final DataHolder dataHolder) + public ObjectInputStream createObjectInputStream(final HierarchicalStreamReader reader, DataHolder dataHolder) throws IOException { + if (collectionUpdateLimit >= 0) { + if (dataHolder == null) { + dataHolder = new MapBackedDataHolder(); + } + dataHolder.put(COLLECTION_UPDATE_LIMIT, new Integer(collectionUpdateLimit)); + dataHolder.put(COLLECTION_UPDATE_SECONDS, new Integer(0)); + } + final DataHolder dh = dataHolder; return new CustomObjectInputStream(new CustomObjectInputStream.StreamCallback() { public Object readFromStream() throws EOFException { if (!reader.hasMoreChildren()) { throw new EOFException(); } reader.moveDown(); - final Object result = unmarshal(reader, null, dataHolder); + final Object result = unmarshal(reader, null, dh); reader.moveUp(); return result; } diff --git a/xstream/src/java/com/thoughtworks/xstream/converters/collections/CollectionConverter.java b/xstream/src/java/com/thoughtworks/xstream/converters/collections/CollectionConverter.java index 94474193..f3606f1d 100644 --- a/xstream/src/java/com/thoughtworks/xstream/converters/collections/CollectionConverter.java +++ b/xstream/src/java/com/thoughtworks/xstream/converters/collections/CollectionConverter.java @@ -1,6 +1,6 @@ /* * Copyright (C) 2003, 2004, 2005 Joe Walnes. - * Copyright (C) 2006, 2007, 2010, 2011, 2013, 2018 XStream Committers. + * Copyright (C) 2006, 2007, 2010, 2011, 2013, 2018, 2021 XStream Committers. * All rights reserved. * * The software in this package is published under the terms of the BSD @@ -13,6 +13,7 @@ import com.thoughtworks.xstream.converters.MarshallingContext; import com.thoughtworks.xstream.converters.UnmarshallingContext; +import com.thoughtworks.xstream.core.SecurityUtils; import com.thoughtworks.xstream.io.HierarchicalStreamReader; import com.thoughtworks.xstream.io.HierarchicalStreamWriter; import com.thoughtworks.xstream.mapper.Mapper; @@ -96,7 +97,10 @@ protected void populateCollection(HierarchicalStreamReader reader, Unmarshalling protected void addCurrentElementToCollection(HierarchicalStreamReader reader, UnmarshallingContext context, Collection collection, Collection target) { final Object item = readItem(reader, context, collection); // call readBareItem when deprecated method is removed + + long now = System.currentTimeMillis(); target.add(item); + SecurityUtils.checkForCollectionDoSAttack(context, now); } protected Object createCollection(Class type) { diff --git a/xstream/src/java/com/thoughtworks/xstream/converters/collections/MapConverter.java b/xstream/src/java/com/thoughtworks/xstream/converters/collections/MapConverter.java index af007f95..f93cec8e 100644 --- a/xstream/src/java/com/thoughtworks/xstream/converters/collections/MapConverter.java +++ b/xstream/src/java/com/thoughtworks/xstream/converters/collections/MapConverter.java @@ -1,6 +1,6 @@ /* * Copyright (C) 2003, 2004, 2005 Joe Walnes. - * Copyright (C) 2006, 2007, 2008, 2010, 2011, 2012, 2013, 2018 XStream Committers. + * Copyright (C) 2006, 2007, 2008, 2010, 2011, 2012, 2013, 2018, 2021 XStream Committers. * All rights reserved. * * The software in this package is published under the terms of the BSD @@ -13,6 +13,7 @@ import com.thoughtworks.xstream.converters.MarshallingContext; import com.thoughtworks.xstream.converters.UnmarshallingContext; +import com.thoughtworks.xstream.core.SecurityUtils; import com.thoughtworks.xstream.io.ExtendedHierarchicalStreamWriterHelper; import com.thoughtworks.xstream.io.HierarchicalStreamReader; import com.thoughtworks.xstream.io.HierarchicalStreamWriter; @@ -104,7 +105,10 @@ protected void putCurrentEntryIntoMap(HierarchicalStreamReader reader, Unmarshal Map map, Map target) { final Object key = readCompleteItem(reader, context, map); final Object value = readCompleteItem(reader, context, map); + + long now = System.currentTimeMillis(); target.put(key, value); + SecurityUtils.checkForCollectionDoSAttack(context, now); } protected Object createCollection(Class type) { diff --git a/xstream/src/java/com/thoughtworks/xstream/converters/extended/NamedMapConverter.java b/xstream/src/java/com/thoughtworks/xstream/converters/extended/NamedMapConverter.java index 4c5ec9cf..59021489 100644 --- a/xstream/src/java/com/thoughtworks/xstream/converters/extended/NamedMapConverter.java +++ b/xstream/src/java/com/thoughtworks/xstream/converters/extended/NamedMapConverter.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013, 2016, 2018 XStream Committers. + * Copyright (C) 2013, 2016, 2018, 2021 XStream Committers. * All rights reserved. * * The software in this package is published under the terms of the BSD @@ -21,6 +21,7 @@ import com.thoughtworks.xstream.converters.UnmarshallingContext; import com.thoughtworks.xstream.converters.collections.MapConverter; import com.thoughtworks.xstream.core.JVM; +import com.thoughtworks.xstream.core.SecurityUtils; import com.thoughtworks.xstream.core.util.HierarchicalStreams; import com.thoughtworks.xstream.io.ExtendedHierarchicalStreamWriterHelper; import com.thoughtworks.xstream.io.HierarchicalStreamReader; @@ -339,7 +340,9 @@ protected void populateMap(HierarchicalStreamReader reader, UnmarshallingContext value = valueConverter.fromString(reader.getValue()); } + long now = System.currentTimeMillis(); target.put(key, value); + SecurityUtils.checkForCollectionDoSAttack(context, now); if (entryName != null) { reader.moveUp(); diff --git a/xstream/src/java/com/thoughtworks/xstream/core/SecurityUtils.java b/xstream/src/java/com/thoughtworks/xstream/core/SecurityUtils.java new file mode 100644 index 00000000..0eedd523 --- /dev/null +++ b/xstream/src/java/com/thoughtworks/xstream/core/SecurityUtils.java @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2021 XStream Committers. + * All rights reserved. + * + * The software in this package is published under the terms of the BSD + * style license a copy of which has been included with this distribution in + * the LICENSE.txt file. + * + * Created on 21. September 2021 by Joerg Schaible + */ +package com.thoughtworks.xstream.core; + +import com.thoughtworks.xstream.XStream; +import com.thoughtworks.xstream.converters.ConversionException; +import com.thoughtworks.xstream.converters.UnmarshallingContext; +import com.thoughtworks.xstream.security.InputManipulationException; + + +/** + * Utility functions for security issues. + * + * @author Jörg Schaible + * @since upcoming + */ +public class SecurityUtils { + + /** + * Check the consumed time adding elements to collections or maps. + * + * Every custom converter should call this method after an unmarshalled element has been added to a collection or + * map. In case of an attack the operation will take too long, because the calculation of the hash code or the + * comparison of the elements in the collection operate on recursive structures. + * + * @param context the unmarshalling context + * @param start the timestamp just before the element was added to the collection or map + * @since upcoming + */ + public static void checkForCollectionDoSAttack(final UnmarshallingContext context, final long start) { + final int diff = (int)((System.currentTimeMillis() - start) / 1000); + if (diff > 0) { + final Integer secondsUsed = (Integer)context.get(XStream.COLLECTION_UPDATE_SECONDS); + if (secondsUsed != null) { + final Integer limit = (Integer)context.get(XStream.COLLECTION_UPDATE_LIMIT); + if (limit == null) { + throw new ConversionException("Missing limit for updating collections."); + } + final int seconds = secondsUsed.intValue() + diff; + if (seconds > limit.intValue()) { + throw new InputManipulationException( + "Denial of Service attack assumed. Adding elements to collections or maps exceeds " + limit.intValue() + " seconds."); + } + context.put(XStream.COLLECTION_UPDATE_SECONDS, new Integer(seconds)); + } + } + } +} diff --git a/xstream/src/java/com/thoughtworks/xstream/core/TreeUnmarshaller.java b/xstream/src/java/com/thoughtworks/xstream/core/TreeUnmarshaller.java index a17363f4..be1ef0d7 100644 --- a/xstream/src/java/com/thoughtworks/xstream/core/TreeUnmarshaller.java +++ b/xstream/src/java/com/thoughtworks/xstream/core/TreeUnmarshaller.java @@ -26,6 +26,7 @@ import com.thoughtworks.xstream.core.util.PrioritizedList; import com.thoughtworks.xstream.io.HierarchicalStreamReader; import com.thoughtworks.xstream.mapper.Mapper; +import com.thoughtworks.xstream.security.AbstractSecurityException; public class TreeUnmarshaller implements UnmarshallingContext { @@ -74,6 +75,8 @@ protected Object convert(Object parent, Class type, Converter converter) { } catch (final ConversionException conversionException) { addInformationTo(conversionException, type, converter, parent); throw conversionException; + } catch (AbstractSecurityException e) { + throw e; } catch (RuntimeException e) { ConversionException conversionException = new ConversionException(e); addInformationTo(conversionException, type, converter, parent); diff --git a/xstream/src/java/com/thoughtworks/xstream/security/AbstractSecurityException.java b/xstream/src/java/com/thoughtworks/xstream/security/AbstractSecurityException.java new file mode 100644 index 00000000..3ca6309c --- /dev/null +++ b/xstream/src/java/com/thoughtworks/xstream/security/AbstractSecurityException.java @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2021 XStream Committers. + * All rights reserved. + * + * Created on 21. September 2021 by Joerg Schaible + */ +package com.thoughtworks.xstream.security; + +import com.thoughtworks.xstream.XStreamException; + + +/** + * General base class for a Security Exception in XStream. + * + * @author Jörg Schaible + * @since upcoming + */ +public abstract class AbstractSecurityException extends XStreamException { + private static final long serialVersionUID = 20210921L; + + /** + * Constructs a SecurityException. + * @param message the exception message + * @since upcoming + */ + public AbstractSecurityException(final String message) { + super(message); + } +} diff --git a/xstream/src/java/com/thoughtworks/xstream/security/ForbiddenClassException.java b/xstream/src/java/com/thoughtworks/xstream/security/ForbiddenClassException.java index 017fc301..2eded6cf 100644 --- a/xstream/src/java/com/thoughtworks/xstream/security/ForbiddenClassException.java +++ b/xstream/src/java/com/thoughtworks/xstream/security/ForbiddenClassException.java @@ -1,20 +1,18 @@ /* - * Copyright (C) 2014 XStream Committers. + * Copyright (C) 2014, 2021 XStream Committers. * All rights reserved. * * Created on 08. January 2014 by Joerg Schaible */ package com.thoughtworks.xstream.security; -import com.thoughtworks.xstream.XStreamException; - /** * Exception thrown for a forbidden class. * * @author Jörg Schaible * @since 1.4.7 */ -public class ForbiddenClassException extends XStreamException { +public class ForbiddenClassException extends AbstractSecurityException { /** * Construct a ForbiddenClassException. diff --git a/xstream/src/java/com/thoughtworks/xstream/security/InputManipulationException.java b/xstream/src/java/com/thoughtworks/xstream/security/InputManipulationException.java new file mode 100644 index 00000000..2d87f660 --- /dev/null +++ b/xstream/src/java/com/thoughtworks/xstream/security/InputManipulationException.java @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2021 XStream Committers. + * All rights reserved. + * + * Created on 21. September 2021 by Joerg Schaible + */ +package com.thoughtworks.xstream.security; + + +/** + * Class for a Security Exception assuming input manipulation in XStream. + * + * @author Jörg Schaible + * @since upcoming + */ +public class InputManipulationException extends AbstractSecurityException { + private static final long serialVersionUID = 20210921L; + + /** + * Constructs a SecurityException. + * @param message the exception message + * @since upcoming + */ + public InputManipulationException(final String message) { + super(message); + } +} diff --git a/xstream/src/test/com/thoughtworks/acceptance/SecurityVulnerabilityTest.java b/xstream/src/test/com/thoughtworks/acceptance/SecurityVulnerabilityTest.java index 09b96a8d..167939d7 100644 --- a/xstream/src/test/com/thoughtworks/acceptance/SecurityVulnerabilityTest.java +++ b/xstream/src/test/com/thoughtworks/acceptance/SecurityVulnerabilityTest.java @@ -17,13 +17,20 @@ import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Hashtable; import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.Map; +import java.util.Set; -import com.thoughtworks.xstream.XStreamException; import com.thoughtworks.xstream.converters.ConversionException; import com.thoughtworks.xstream.core.JVM; import com.thoughtworks.xstream.security.AnyTypePermission; import com.thoughtworks.xstream.security.ForbiddenClassException; +import com.thoughtworks.xstream.security.InputManipulationException; import com.thoughtworks.xstream.security.ProxyTypePermission; @@ -56,9 +63,9 @@ public void testCannotInjectEventHandler() { try { xstream.fromXML(xml); - fail("Thrown " + XStreamException.class.getName() + " expected"); - } catch (final XStreamException e) { - assertTrue(e.getMessage().indexOf(EventHandler.class.getName()) > 0); + fail("Thrown " + ForbiddenClassException.class.getName() + " expected"); + } catch (final ForbiddenClassException e) { + // OK } assertEquals(0, BUFFER.length()); } @@ -126,7 +133,7 @@ public void exec() { public void testInstanceOfVoid() { try { xstream.fromXML("<void/>"); - fail("Thrown " + ConversionException.class.getName() + " expected"); + fail("Thrown " + ForbiddenClassException.class.getName() + " expected"); } catch (final ForbiddenClassException e) { // OK } @@ -163,7 +170,7 @@ public void testCannotUseJaxwsInputStreamToDeleteFile() { xstream.aliasType("is", InputStream.class); try { xstream.fromXML(xml); - fail("Thrown " + ConversionException.class.getName() + " expected"); + fail("Thrown " + ForbiddenClassException.class.getName() + " expected"); } catch (final ForbiddenClassException e) { // OK } @@ -261,4 +268,140 @@ public void testExplicitlyUnmarshalEndlessByteArryInputStream() { assertEquals("ArrayIndexOutOfBoundsException expected reading invalid stream", 5, i); } } + + public void testDoSAttackWithHashSet() { + final Set set = new HashSet(); + Set s1 = set; + Set s2 = new HashSet(); + for (int i = 0; i < 30; i++) { + final Set t1 = new HashSet(); + final Set t2 = new HashSet(); + t1.add("a"); + t2.add("b"); + s1.add(t1); + s1.add(t2); + s2.add(t2); + s2.add(t1); + s1 = t1; + s2 = t2; + } + + xstream.setCollectionUpdateLimit(5); + final String xml = xstream.toXML(set); + try { + + xstream.fromXML(xml); + fail("Thrown " + InputManipulationException.class.getName() + " expected"); + } catch (final InputManipulationException e) { + assertTrue("Limit expected in message", e.getMessage().contains("exceeds 5 seconds")); + } + } + + public void testDoSAttackWithLinkedHashSet() { + final Set set = new LinkedHashSet(); + Set s1 = set; + Set s2 = new LinkedHashSet(); + for (int i = 0; i < 30; i++) { + final Set t1 = new LinkedHashSet(); + final Set t2 = new LinkedHashSet(); + t1.add("a"); + t2.add("b"); + s1.add(t1); + s1.add(t2); + s2.add(t2); + s2.add(t1); + s1 = t1; + s2 = t2; + } + + xstream.setCollectionUpdateLimit(5); + final String xml = xstream.toXML(set); + try { + xstream.fromXML(xml); + fail("Thrown " + InputManipulationException.class.getName() + " expected"); + } catch (final InputManipulationException e) { + assertTrue("Limit expected in message", e.getMessage().contains("exceeds 5 seconds")); + } + } + + public void testDoSAttackWithHashMap() { + final Map map = new HashMap(); + Map m1 = map; + Map m2 = new HashMap(); + for (int i = 0; i < 25; i++) { + final Map t1 = new HashMap(); + final Map t2 = new HashMap(); + t1.put("a", "b"); + t2.put("c", "d"); + m1.put(t1, t2); + m1.put(t2, t1); + m2.put(t2, t1); + m2.put(t1, t2); + m1 = t1; + m2 = t2; + } + xstream.setCollectionUpdateLimit(5); + + final String xml = xstream.toXML(map); + try { + xstream.fromXML(xml); + fail("Thrown " + InputManipulationException.class.getName() + " expected"); + } catch (InputManipulationException e) { + assertTrue("Limit expected in message", e.getMessage().contains("exceeds 5 seconds")); + } + } + + public void testDoSAttackWithLinkedHashMap() { + final Map map = new LinkedHashMap(); + Map m1 = map; + Map m2 = new LinkedHashMap(); + for (int i = 0; i < 25; i++) { + final Map t1 = new LinkedHashMap(); + final Map t2 = new LinkedHashMap(); + t1.put("a", "b"); + t2.put("c", "d"); + m1.put(t1, t2); + m1.put(t2, t1); + m2.put(t2, t1); + m2.put(t1, t2); + m1 = t1; + m2 = t2; + } + + xstream.setCollectionUpdateLimit(5); + final String xml = xstream.toXML(map); + try { + xstream.fromXML(xml); + fail("Thrown " + InputManipulationException.class.getName() + " expected"); + } catch (final InputManipulationException e) { + assertTrue("Limit expected in message", e.getMessage().contains("exceeds 5 seconds")); + } + } + + public void testDoSAttackWithHashtable() { + final Map map = new Hashtable(); + Map m1 = map; + Map m2 = new Hashtable(); + for (int i = 0; i < 100; i++) { + final Map t1 = new Hashtable(); + final Map t2 = new Hashtable(); + t1.put("a", "b"); + t2.put("c", "d"); + m1.put(t1, t2); + m1.put(t2, t1); + m2.put(t2, t1); + m2.put(t1, t2); + m1 = t1; + m2 = t2; + } + + xstream.setCollectionUpdateLimit(5); + final String xml = xstream.toXML(map); + try { + xstream.fromXML(xml); + fail("Thrown " + InputManipulationException.class.getName() + " expected"); + } catch (final InputManipulationException e) { + assertTrue("Limit expected in message", e.getMessage().contains("exceeds 5 seconds")); + } + } }
Locations
Projects
Search
Status Monitor
Help
Open Build Service
OBS Manuals
API Documentation
OBS Portal
Reporting a Bug
Contact
Mailing List
Forums
Chat (IRC)
Twitter
Open Build Service (OBS)
is an
openSUSE project
.
浙ICP备2022010568号-2