From 448087c001fd302df4a8b73f1dd87631c2ae0e61 Mon Sep 17 00:00:00 2001 From: Marian Koncek Date: Wed, 14 Oct 2020 15:09:29 +0200 Subject: [PATCH] CVE-2015-7501 --- .../functors/CloneTransformer.java | 32 ++++++++++++- .../collections4/functors/ForClosure.java | 33 ++++++++++++- .../collections4/functors/FunctorUtils.java | 35 ++++++++++++++ .../functors/InstantiateFactory.java | 32 ++++++++++++- .../functors/InstantiateTransformer.java | 31 +++++++++++++ .../functors/InvokerTransformer.java | 32 ++++++++++++- .../functors/PrototypeFactory.java | 46 +++++++++++++++++++ .../collections4/functors/WhileClosure.java | 33 ++++++++++++- .../collections4/FactoryUtilsTest.java | 35 -------------- .../collections4/TransformerUtilsTest.java | 1 - 10 files changed, 269 insertions(+), 41 deletions(-) diff --git a/src/main/java/org/apache/commons/collections4/functors/CloneTransformer.java b/src/main/java/org/apache/commons/collections4/functors/CloneTransformer.java index 87b080b..90fb3ef 100644 --- a/src/main/java/org/apache/commons/collections4/functors/CloneTransformer.java +++ b/src/main/java/org/apache/commons/collections4/functors/CloneTransformer.java @@ -16,6 +16,9 @@ */ package org.apache.commons.collections4.functors; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; import java.io.Serializable; import org.apache.commons.collections4.Transformer; @@ -24,7 +27,17 @@ import org.apache.commons.collections4.Transformer; * Transformer implementation that returns a clone of the input object. *

* Clone is performed using PrototypeFactory.prototypeFactory(input).create(). - * + *

+ * WARNING: This class will throw an + * {@link UnsupportedOperationException} when trying to serialize or + * de-serialize an instance to prevent potential remote code execution exploits. + *

+ * In order to re-enable serialization support for {@code CloneTransformer} + * the following system property can be used (via -Dproperty=true): + *

+ * org.apache.commons.collections.enableUnsafeSerialization
+ * 
+ * * @since 3.0 * @version $Id: CloneTransformer.java 1543950 2013-11-20 21:13:35Z tn $ */ @@ -73,4 +86,21 @@ public class CloneTransformer implements Transformer, Serializable { return INSTANCE; } + /** + * Overrides the default writeObject implementation to prevent + * serialization (see COLLECTIONS-580). + */ + private void writeObject(ObjectOutputStream os) throws IOException { + FunctorUtils.checkUnsafeSerialization(CloneTransformer.class); + os.defaultWriteObject(); + } + + /** + * Overrides the default readObject implementation to prevent + * de-serialization (see COLLECTIONS-580). + */ + private void readObject(ObjectInputStream is) throws ClassNotFoundException, IOException { + FunctorUtils.checkUnsafeSerialization(CloneTransformer.class); + is.defaultReadObject(); + } } diff --git a/src/main/java/org/apache/commons/collections4/functors/ForClosure.java b/src/main/java/org/apache/commons/collections4/functors/ForClosure.java index ef5a4d1..fdf1d80 100644 --- a/src/main/java/org/apache/commons/collections4/functors/ForClosure.java +++ b/src/main/java/org/apache/commons/collections4/functors/ForClosure.java @@ -16,13 +16,26 @@ */ package org.apache.commons.collections4.functors; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; import java.io.Serializable; import org.apache.commons.collections4.Closure; /** * Closure implementation that calls another closure n times, like a for loop. - * + *

+ * WARNING: This class will throw an + * {@link UnsupportedOperationException} when trying to serialize or + * de-serialize an instance to prevent potential remote code execution exploits. + *

+ * In order to re-enable serialization support for {@code ForClosure} + * the following system property can be used (via -Dproperty=true): + *

+ * org.apache.commons.collections.enableUnsafeSerialization
+ * 
+ * * @since 3.0 * @version $Id: ForClosure.java 1477798 2013-04-30 19:49:02Z tn $ */ @@ -102,4 +115,22 @@ public class ForClosure implements Closure, Serializable { return iCount; } + /** + * Overrides the default writeObject implementation to prevent + * serialization (see COLLECTIONS-580). + */ + private void writeObject(ObjectOutputStream os) throws IOException { + FunctorUtils.checkUnsafeSerialization(ForClosure.class); + os.defaultWriteObject(); + } + + /** + * Overrides the default readObject implementation to prevent + * de-serialization (see COLLECTIONS-580). + */ + private void readObject(ObjectInputStream is) throws ClassNotFoundException, IOException { + FunctorUtils.checkUnsafeSerialization(ForClosure.class); + is.defaultReadObject(); + } + } diff --git a/src/main/java/org/apache/commons/collections4/functors/FunctorUtils.java b/src/main/java/org/apache/commons/collections4/functors/FunctorUtils.java index de3ccbd..57da174 100644 --- a/src/main/java/org/apache/commons/collections4/functors/FunctorUtils.java +++ b/src/main/java/org/apache/commons/collections4/functors/FunctorUtils.java @@ -16,6 +16,8 @@ */ package org.apache.commons.collections4.functors; +import java.security.AccessController; +import java.security.PrivilegedAction; import java.util.Collection; import org.apache.commons.collections4.Closure; @@ -30,6 +32,10 @@ import org.apache.commons.collections4.Transformer; */ class FunctorUtils { + /** System property key to enable unsafe serialization */ + final static String UNSAFE_SERIALIZABLE_PROPERTY + = "org.apache.commons.collections.enableUnsafeSerialization"; + /** * Restricted constructor. */ @@ -204,4 +210,33 @@ class FunctorUtils { return (Transformer) transformer; } + /** + * Package-private helper method to check if serialization support is + * enabled for unsafe classes. + * + * @param clazz the clazz to check for serialization support + * @throws UnsupportedOperationException if unsafe serialization is disabled + */ + static void checkUnsafeSerialization(Class clazz) { + String unsafeSerializableProperty; + + try { + unsafeSerializableProperty = + (String) AccessController.doPrivileged(new PrivilegedAction() { + public Object run() { + return System.getProperty(UNSAFE_SERIALIZABLE_PROPERTY); + } + }); + } catch (SecurityException ex) { + unsafeSerializableProperty = null; + } + + if (!"true".equalsIgnoreCase(unsafeSerializableProperty)) { + throw new UnsupportedOperationException( + "Serialization support for " + clazz.getName() + " is disabled for security reasons. " + + "To enable it set system property '" + UNSAFE_SERIALIZABLE_PROPERTY + "' to 'true', " + + "but you must ensure that your application does not de-serialize objects from untrusted sources."); + } + } + } diff --git a/src/main/java/org/apache/commons/collections4/functors/InstantiateFactory.java b/src/main/java/org/apache/commons/collections4/functors/InstantiateFactory.java index dd6c548..a9fb91f 100644 --- a/src/main/java/org/apache/commons/collections4/functors/InstantiateFactory.java +++ b/src/main/java/org/apache/commons/collections4/functors/InstantiateFactory.java @@ -16,6 +16,9 @@ */ package org.apache.commons.collections4.functors; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; import java.io.Serializable; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; @@ -25,7 +28,17 @@ import org.apache.commons.collections4.FunctorException; /** * Factory implementation that creates a new object instance by reflection. - * + *

+ * WARNING: This class will throw an + * {@link UnsupportedOperationException} when trying to serialize or + * de-serialize an instance to prevent potential remote code execution exploits. + *

+ * In order to re-enable serialization support for {@code InstantiateTransformer} + * the following system property can be used (via -Dproperty=true): + *

+ * org.apache.commons.collections.enableUnsafeSerialization
+ * 
+ * * @since 3.0 * @version $Id: InstantiateFactory.java 1477798 2013-04-30 19:49:02Z tn $ */ @@ -133,4 +146,21 @@ public class InstantiateFactory implements Factory, Serializable { } } + /** + * Overrides the default writeObject implementation to prevent + * serialization (see COLLECTIONS-580). + */ + private void writeObject(ObjectOutputStream os) throws IOException { + FunctorUtils.checkUnsafeSerialization(InstantiateFactory.class); + os.defaultWriteObject(); + } + + /** + * Overrides the default readObject implementation to prevent + * de-serialization (see COLLECTIONS-580). + */ + private void readObject(ObjectInputStream is) throws ClassNotFoundException, IOException { + FunctorUtils.checkUnsafeSerialization(InstantiateFactory.class); + is.defaultReadObject(); + } } diff --git a/src/main/java/org/apache/commons/collections4/functors/InstantiateTransformer.java b/src/main/java/org/apache/commons/collections4/functors/InstantiateTransformer.java index ce3d662..0595316 100644 --- a/src/main/java/org/apache/commons/collections4/functors/InstantiateTransformer.java +++ b/src/main/java/org/apache/commons/collections4/functors/InstantiateTransformer.java @@ -16,6 +16,9 @@ */ package org.apache.commons.collections4.functors; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; import java.io.Serializable; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; @@ -25,6 +28,16 @@ import org.apache.commons.collections4.Transformer; /** * Transformer implementation that creates a new object instance by reflection. + *

+ * WARNING: This class will throw an + * {@link UnsupportedOperationException} when trying to serialize or + * de-serialize an instance to prevent potential remote code execution exploits. + *

+ * In order to re-enable serialization support for {@code InstantiateTransformer} + * the following system property can be used (via -Dproperty=true): + *

+ * org.apache.commons.collections.enableUnsafeSerialization
+ * 
* * @since 3.0 * @version $Id: InstantiateTransformer.java 1543950 2013-11-20 21:13:35Z tn $ @@ -125,4 +138,22 @@ public class InstantiateTransformer implements Transformer } } + /** + * Overrides the default writeObject implementation to prevent + * serialization (see COLLECTIONS-580). + */ + private void writeObject(ObjectOutputStream os) throws IOException { + FunctorUtils.checkUnsafeSerialization(InstantiateTransformer.class); + os.defaultWriteObject(); + } + + /** + * Overrides the default readObject implementation to prevent + * de-serialization (see COLLECTIONS-580). + */ + private void readObject(ObjectInputStream is) throws ClassNotFoundException, IOException { + FunctorUtils.checkUnsafeSerialization(InstantiateTransformer.class); + is.defaultReadObject(); + } + } diff --git a/src/main/java/org/apache/commons/collections4/functors/InvokerTransformer.java b/src/main/java/org/apache/commons/collections4/functors/InvokerTransformer.java index bd2d67d..f77f164 100644 --- a/src/main/java/org/apache/commons/collections4/functors/InvokerTransformer.java +++ b/src/main/java/org/apache/commons/collections4/functors/InvokerTransformer.java @@ -16,6 +16,9 @@ */ package org.apache.commons.collections4.functors; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; import java.io.Serializable; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; @@ -25,7 +28,17 @@ import org.apache.commons.collections4.Transformer; /** * Transformer implementation that creates a new object instance by reflection. - * + *

+ * WARNING: This class will throw an + * {@link UnsupportedOperationException} when trying to serialize or + * de-serialize an instance to prevent potential remote code execution exploits. + *

+ * In order to re-enable serialization support for {@code InvokerTransformer} + * the following system property can be used (via -Dproperty=true): + *

+ * org.apache.commons.collections.enableUnsafeSerialization
+ * 
+ * * @since 3.0 * @version $Id: InvokerTransformer.java 1543247 2013-11-19 00:36:51Z ggregory $ */ @@ -139,4 +152,21 @@ public class InvokerTransformer implements Transformer, Serializable } } + /** + * Overrides the default writeObject implementation to prevent + * serialization (see COLLECTIONS-580). + */ + private void writeObject(ObjectOutputStream os) throws IOException { + FunctorUtils.checkUnsafeSerialization(InvokerTransformer.class); + os.defaultWriteObject(); + } + + /** + * Overrides the default readObject implementation to prevent + * de-serialization (see COLLECTIONS-580). + */ + private void readObject(ObjectInputStream is) throws ClassNotFoundException, IOException { + FunctorUtils.checkUnsafeSerialization(InvokerTransformer.class); + is.defaultReadObject(); + } } diff --git a/src/main/java/org/apache/commons/collections4/functors/PrototypeFactory.java b/src/main/java/org/apache/commons/collections4/functors/PrototypeFactory.java index 8922d15..696fa35 100644 --- a/src/main/java/org/apache/commons/collections4/functors/PrototypeFactory.java +++ b/src/main/java/org/apache/commons/collections4/functors/PrototypeFactory.java @@ -47,6 +47,16 @@ public class PrototypeFactory { *
  • public copy constructor *
  • serialization clone *
      + *

      + * WARNING: This method will return a {@code Factory} + * that will throw an {@link UnsupportedOperationException} when trying to serialize + * or de-serialize it to prevent potential remote code execution exploits. + *

      + * In order to re-enable serialization support the following system property + * can be used (via -Dproperty=true): + *

      +     * org.apache.commons.collections.enableUnsafeSerialization
      +     * 
      * * @param the type the factory creates * @param prototype the object to clone each time in the factory @@ -141,6 +151,24 @@ public class PrototypeFactory { throw new FunctorException("PrototypeCloneFactory: Clone method threw an exception", ex); } } + + /** + * Overrides the default writeObject implementation to prevent + * serialization (see COLLECTIONS-580). + */ + private void writeObject(ObjectOutputStream os) throws IOException { + FunctorUtils.checkUnsafeSerialization(PrototypeCloneFactory.class); + os.defaultWriteObject(); + } + + /** + * Overrides the default readObject implementation to prevent + * de-serialization (see COLLECTIONS-580). + */ + private void readObject(ObjectInputStream is) throws ClassNotFoundException, IOException { + FunctorUtils.checkUnsafeSerialization(PrototypeCloneFactory.class); + is.defaultReadObject(); + } } // PrototypeSerializationFactory @@ -200,6 +228,24 @@ public class PrototypeFactory { } } } + + /** + * Overrides the default writeObject implementation to prevent + * serialization (see COLLECTIONS-580). + */ + private void writeObject(ObjectOutputStream os) throws IOException { + FunctorUtils.checkUnsafeSerialization(PrototypeSerializationFactory.class); + os.defaultWriteObject(); + } + + /** + * Overrides the default readObject implementation to prevent + * de-serialization (see COLLECTIONS-580). + */ + private void readObject(ObjectInputStream is) throws ClassNotFoundException, IOException { + FunctorUtils.checkUnsafeSerialization(PrototypeSerializationFactory.class); + is.defaultReadObject(); + } } } diff --git a/src/main/java/org/apache/commons/collections4/functors/WhileClosure.java b/src/main/java/org/apache/commons/collections4/functors/WhileClosure.java index b169d44..8c8eb91 100644 --- a/src/main/java/org/apache/commons/collections4/functors/WhileClosure.java +++ b/src/main/java/org/apache/commons/collections4/functors/WhileClosure.java @@ -16,6 +16,9 @@ */ package org.apache.commons.collections4.functors; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; import java.io.Serializable; import org.apache.commons.collections4.Closure; @@ -24,7 +27,17 @@ import org.apache.commons.collections4.Predicate; /** * Closure implementation that executes a closure repeatedly until a condition is met, * like a do-while or while loop. - * + *

      + * WARNING: This class will throw an + * {@link UnsupportedOperationException} when trying to serialize or + * de-serialize an instance to prevent potential remote code execution exploits. + *

      + * In order to re-enable serialization support for {@code WhileClosure} + * the following system property can be used (via -Dproperty=true): + *

      + * org.apache.commons.collections.enableUnsafeSerialization
      + * 
      + * * @since 3.0 * @version $Id: WhileClosure.java 1477798 2013-04-30 19:49:02Z tn $ */ @@ -120,4 +133,22 @@ public class WhileClosure implements Closure, Serializable { return iDoLoop; } + /** + * Overrides the default writeObject implementation to prevent + * serialization (see COLLECTIONS-580). + */ + private void writeObject(ObjectOutputStream os) throws IOException { + FunctorUtils.checkUnsafeSerialization(WhileClosure.class); + os.defaultWriteObject(); + } + + /** + * Overrides the default readObject implementation to prevent + * de-serialization (see COLLECTIONS-580). + */ + private void readObject(ObjectInputStream is) throws ClassNotFoundException, IOException { + FunctorUtils.checkUnsafeSerialization(WhileClosure.class); + is.defaultReadObject(); + } + } diff --git a/src/test/java/org/apache/commons/collections4/FactoryUtilsTest.java b/src/test/java/org/apache/commons/collections4/FactoryUtilsTest.java index 941cd40..7394ede 100644 --- a/src/test/java/org/apache/commons/collections4/FactoryUtilsTest.java +++ b/src/test/java/org/apache/commons/collections4/FactoryUtilsTest.java @@ -112,15 +112,6 @@ public class FactoryUtilsTest extends junit.framework.TestCase { final Date created = factory.create(); assertTrue(proto != created); assertEquals(proto, created); - - // check serialisation works - final ByteArrayOutputStream buffer = new ByteArrayOutputStream(); - final ObjectOutputStream out = new ObjectOutputStream(buffer); - out.writeObject(factory); - out.close(); - final ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(buffer.toByteArray())); - in.readObject(); - in.close(); } public void testPrototypeFactoryPublicCopyConstructor() throws Exception { @@ -130,23 +121,6 @@ public class FactoryUtilsTest extends junit.framework.TestCase { final Object created = factory.create(); assertTrue(proto != created); assertEquals(proto, created); - - // check serialisation works - ByteArrayOutputStream buffer = new ByteArrayOutputStream(); - ObjectOutputStream out = new ObjectOutputStream(buffer); - try { - out.writeObject(factory); - } catch (final NotSerializableException ex) { - out.close(); - } - factory = FactoryUtils.prototypeFactory(new Mock2("S")); - buffer = new ByteArrayOutputStream(); - out = new ObjectOutputStream(buffer); - out.writeObject(factory); - out.close(); - final ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(buffer.toByteArray())); - in.readObject(); - in.close(); } public void testPrototypeFactoryPublicSerialization() throws Exception { @@ -156,15 +130,6 @@ public class FactoryUtilsTest extends junit.framework.TestCase { final Integer created = factory.create(); assertTrue(proto != created); assertEquals(proto, created); - - // check serialisation works - final ByteArrayOutputStream buffer = new ByteArrayOutputStream(); - final ObjectOutputStream out = new ObjectOutputStream(buffer); - out.writeObject(factory); - out.close(); - final ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(buffer.toByteArray())); - in.readObject(); - in.close(); } public void testPrototypeFactoryPublicSerializationError() { diff --git a/src/test/java/org/apache/commons/collections4/TransformerUtilsTest.java b/src/test/java/org/apache/commons/collections4/TransformerUtilsTest.java index 9c0c530..a924e05 100644 --- a/src/test/java/org/apache/commons/collections4/TransformerUtilsTest.java +++ b/src/test/java/org/apache/commons/collections4/TransformerUtilsTest.java @@ -431,7 +431,6 @@ public class TransformerUtilsTest extends junit.framework.TestCase { */ public void testSingletonPatternInSerialization() { final Object[] singletones = new Object[] { - CloneTransformer.INSTANCE, ExceptionTransformer.INSTANCE, NOPTransformer.INSTANCE, StringValueTransformer.stringValueTransformer(), -- 2.26.2