From: Markus Koschany <apo@debian.org>
Date: Sat, 3 Apr 2021 21:15:39 +0200
Subject: CVE-2021-21341-to-CVE-2021-21351
Bug-Debian: https://bugs.debian.org/985843
Origin: https://github.com/x-stream/xstream/commit/d5e51177634afea7213b9dc2d21f101d2e258db9
---
.../src/java/com/thoughtworks/xstream/XStream.java | 32 +++++++++++++---
.../acceptance/SecurityVulnerabilityTest.java | 43 ++++++++++++++++++++++
2 files changed, 70 insertions(+), 5 deletions(-)
diff --git a/xstream/src/java/com/thoughtworks/xstream/XStream.java b/xstream/src/java/com/thoughtworks/xstream/XStream.java
index 8415da2..b5e43af 100644
--- a/xstream/src/java/com/thoughtworks/xstream/XStream.java
+++ b/xstream/src/java/com/thoughtworks/xstream/XStream.java
@@ -1,6 +1,6 @@
/*
* Copyright (C) 2003, 2004, 2005, 2006 Joe Walnes.
- * Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2020 XStream Committers.
+ * Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2020, 2021 XStream Committers.
* All rights reserved.
*
* The software in this package is published under the terms of the BSD
@@ -36,7 +36,6 @@ import java.net.URL;
import java.nio.charset.Charset;
import java.text.DecimalFormatSymbols;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.BitSet;
import java.util.Calendar;
import java.util.Collection;
@@ -335,9 +334,14 @@ public class XStream {
private static final String ANNOTATION_MAPPER_TYPE = "com.thoughtworks.xstream.mapper.AnnotationMapper";
private static final Pattern IGNORE_ALL = Pattern.compile(".*");
+ private static final Pattern GETTER_SETTER_REFLECTION = Pattern.compile(".*\\$GetterSetterReflection");
+ private static final Pattern PRIVILEGED_GETTER = Pattern.compile(".*\\$PrivilegedGetter");
private static final Pattern LAZY_ITERATORS = Pattern.compile(".*\\$LazyIterator");
+ private static final Pattern JAXWS_ITERATORS = Pattern.compile(".*\\$ServiceNameIterator");
+ private static final Pattern JAVAFX_OBSERVABLE_LIST__ = Pattern.compile(
+ "javafx\\.collections\\.ObservableList\\$.*");
private static final Pattern JAVAX_CRYPTO = Pattern.compile("javax\\.crypto\\..*");
- private static final Pattern JAXWS_FILE_STREAM = Pattern.compile(".*\\.ReadAllStream\\$FileStream");
+ private static final Pattern BCEL_CL = Pattern.compile(".*\\.bcel\\..*\\.util\\.ClassLoader");
/**
* Constructs a default XStream.
@@ -647,12 +651,30 @@ public class XStream {
"java.beans.EventHandler", //
"java.lang.ProcessBuilder", //
"javax.imageio.ImageIO$ContainsFilter", //
- "jdk.nashorn.internal.objects.NativeString" });
- denyTypesByRegExp(new Pattern[]{LAZY_ITERATORS, JAVAX_CRYPTO, JAXWS_FILE_STREAM});
+ "jdk.nashorn.internal.objects.NativeString", //
+ "com.sun.corba.se.impl.activation.ServerTableEntry", //
+ "com.sun.tools.javac.processing.JavacProcessingEnvironment$NameProcessIterator", //
+ "sun.awt.datatransfer.DataTransferer$IndexOrderComparator", //
+ "sun.swing.SwingLazyValue"});
+ denyTypesByRegExp(new Pattern[]{
+ LAZY_ITERATORS, GETTER_SETTER_REFLECTION, PRIVILEGED_GETTER, JAVAX_CRYPTO, JAXWS_ITERATORS,
+ JAVAFX_OBSERVABLE_LIST__, BCEL_CL});
+ denyTypeHierarchy(InputStream.class);
+ denyTypeHierarchyDynamically("java.nio.channels.Channel");
+ denyTypeHierarchyDynamically("javax.activation.DataSource");
+ denyTypeHierarchyDynamically("javax.sql.rowset.BaseRowSet");
+
allowTypeHierarchy(Exception.class);
securityInitialized = false;
}
+ private void denyTypeHierarchyDynamically(String className) {
+ Class type = JVM.loadClassForName(className);
+ if (type != null) {
+ denyTypeHierarchy(type);
+ }
+ }
+
/**
* Setup the security framework of a XStream instance.
* <p>
diff --git a/xstream/src/test/com/thoughtworks/acceptance/SecurityVulnerabilityTest.java b/xstream/src/test/com/thoughtworks/acceptance/SecurityVulnerabilityTest.java
index da5f861..9da221c 100644
--- a/xstream/src/test/com/thoughtworks/acceptance/SecurityVulnerabilityTest.java
+++ b/xstream/src/test/com/thoughtworks/acceptance/SecurityVulnerabilityTest.java
@@ -11,6 +11,7 @@
package com.thoughtworks.acceptance;
import java.beans.EventHandler;
+import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
@@ -282,4 +283,46 @@ public class SecurityVulnerabilityTest extends AbstractAcceptanceTest {
}
}
}
+
+ public void testCannotInjectManipulatedByteArryInputStream() {
+ xstream.alias("bais", ByteArrayInputStream.class);
+ System.out.println(Integer.MAX_VALUE);
+ final String xml = ""
+ + "<bais>\n"
+ + " <buf></buf>\n"
+ + " <pos>-2147483648</pos>\n"
+ + " <mark>0</mark>\n"
+ + " <count>0</count>\n"
+ + "</bais>";
+
+ try {
+ xstream.fromXML(xml);
+ fail("Thrown " + ForbiddenClassException.class.getName() + " expected");
+ } catch (final ForbiddenClassException e) {
+ assertEquals(e.getMessage(),ByteArrayInputStream.class.getName());
+ }
+ }
+
+ public void testExplicitlyUnmarshalEndlessByteArryInputStream() {
+ xstream.alias("bais", ByteArrayInputStream.class);
+ xstream.allowTypes(new Class[]{ByteArrayInputStream.class});
+
+ final String xml = ""
+ + "<bais>\n"
+ + " <buf></buf>\n"
+ + " <pos>-2147483648</pos>\n"
+ + " <mark>0</mark>\n"
+ + " <count>0</count>\n"
+ + "</bais>";
+
+ final byte[] data = new byte[10];
+ final ByteArrayInputStream bais = (ByteArrayInputStream)xstream.fromXML(xml);
+ int i = 5;
+ while(bais.read(data, 0, 10) == 0) {
+ if (--i == 0) {
+ break;
+ }
+ }
+ assertEquals("Unlimited reads of ByteArrayInputStream returning 0 bytes expected", 0, i);
+ }
}