CVE-2013-4521: Deserialization of Untrusted Data (CWE-502)
value: RichFaces implementation in Nuxeo Platform 5.6.0 before HF27 and 5.8.0 before HF-01 does not restrict the classes for which deserialization methods can be called, which allows remote attackers to execute arbitrary code via crafted serialized data. NOTE: this vulnerability may overlap CVE-2013-2165.
CWE description: The product deserializes untrusted data without sufficiently verifying that the resulting data will be valid.
CVSS v3 base score: 9.8
Published: 2020-02-06T16:15Z
Repository: https://github.com/nuxeo/richfaces
Language: Java
Files changed: framework/impl/src/main/java/org/ajax4jsf/resource/LookAheadObjectInputStream.java, framework/impl/src/main/java/org/ajax4jsf/resource/ResourceBuilderImpl.java, framework/impl/src/main/java/org/ajax4jsf/resource/SerializableResource.java, framework/impl/src/main/java/org/ajax4jsf/resource/UserResource.java, framework/impl/src/main/resources/org/ajax4jsf/resource/resource-serialization.properties, samples/richfaces-demo/src/main/java/org/richfaces/demo/media/MediaData.java, samples/richfaces-demo/src/main/java/org/richfaces/demo/paint2d/PaintData.java, ui/paint2D/src/main/java/org/richfaces/renderkit/html/Paint2DResource.java
Commit date: 2013-11-07 11:55:52 +0100
Commit message: NXBT-661: apply patch for CVE-2013-4521 flaw

Diff: diff --git a/framework/impl/src/main/java/org/ajax4jsf/resource/LookAheadObjectInputStream.java b/framework/impl/src/main/java/org/ajax4jsf/resource/LookAheadObjectInputStream.java
new file mode 100644
index 0000000..3acce6d
--- /dev/null
+++ b/framework/impl/src/main/java/org/ajax4jsf/resource/LookAheadObjectInputStream.java
@@ -0,0 +1,148 @@
+/**
+ * JBoss, Home of Professional Open Source
+ * Copyright 2010, Red Hat, Inc. and individual contributors
+ * by the @authors tag. See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ **/
+package org.ajax4jsf.resource;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InvalidClassException;
+import java.io.ObjectInputStream;
+import java.io.ObjectStreamClass;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * When deserializing objects, first check that the class being deserialized is in the allowed whitelist.
+ *
+ * @author <a href="http://community.jboss.org/people/bleathem">Brian Leathem</a>
+ */
+public class LookAheadObjectInputStream extends ObjectInputStream {
+    private static final Map<String, Class<?>> PRIMITIVE_TYPES = new HashMap<String, Class<?>>(9, 1.0F);
+    private static Set<Class> whitelistBaseClasses = new HashSet<Class>();
+    private static Set<String> whitelistClassNameCache = Collections.newSetFromMap(new ConcurrentHashMap<String, Boolean>());
+
+    static {
+        PRIMITIVE_TYPES.put("bool", Boolean.TYPE);
+        PRIMITIVE_TYPES.put("byte", Byte.TYPE);
+        PRIMITIVE_TYPES.put("char", Character.TYPE);
+        PRIMITIVE_TYPES.put("short", Short.TYPE);
+        PRIMITIVE_TYPES.put("int", Integer.TYPE);
+        PRIMITIVE_TYPES.put("long", Long.TYPE);
+        PRIMITIVE_TYPES.put("float", Float.TYPE);
+        PRIMITIVE_TYPES.put("double", Double.TYPE);
+        PRIMITIVE_TYPES.put("void", Void.TYPE);
+
+        whitelistClassNameCache.add(new Object[0].getClass().getName());
+        whitelistClassNameCache.add(new String[0].getClass().getName());
+        whitelistClassNameCache.add(new Boolean[0].getClass().getName());
+        whitelistClassNameCache.add(new Byte[0].getClass().getName());
+        whitelistClassNameCache.add(new Character[0].getClass().getName());
+        whitelistClassNameCache.add(new Short[0].getClass().getName());
+        whitelistClassNameCache.add(new Integer[0].getClass().getName());
+        whitelistClassNameCache.add(new Long[0].getClass().getName());
+        whitelistClassNameCache.add(new Float[0].getClass().getName());
+        whitelistClassNameCache.add(new Double[0].getClass().getName());
+        whitelistClassNameCache.add(new Void[0].getClass().getName());
+
+        whitelistBaseClasses.add(String.class);
+        whitelistBaseClasses.add(Boolean.class);
+        whitelistBaseClasses.add(Byte.class);
+        whitelistBaseClasses.add(Character.class);
+        whitelistBaseClasses.add(Number.class);
+
+        loadWhitelist();
+    }
+
+    public LookAheadObjectInputStream(InputStream in) throws IOException {
+        super(in);
+    }
+
+    /**
+     * Only deserialize primitive or whitelisted classes
+     */
+    @Override
+    protected Class<?> resolveClass(ObjectStreamClass desc) throws IOException, ClassNotFoundException {
+        Class<?> primitiveType = PRIMITIVE_TYPES.get(desc.getName());
+        if (primitiveType != null) {
+            return primitiveType;
+        }
+        if (!isClassValid(desc.getName())) {
+            throw new InvalidClassException("Unauthorized deserialization attempt", desc.getName());
+        }
+        return super.resolveClass(desc);
+    }
+
+    /**
+     * Determine if the given requestedClassName is allowed by the whitelist
+     */
+    boolean isClassValid(String requestedClassName) {
+        if (whitelistClassNameCache.contains(requestedClassName)) {
+            return true;
+        }
+        try {
+            Class<?> requestedClass = Class.forName(requestedClassName);
+            for (Class baseClass : whitelistBaseClasses ) {
+                if (baseClass.isAssignableFrom(requestedClass)) {
+                    whitelistClassNameCache.add(requestedClassName);
+                    return true;
+                }
+            }
+        } catch (ClassNotFoundException e) {
+            return false;
+        }
+        return false;
+    }
+
+    /**
+     * Load the whitelist from the properties file
+     */
+    static void loadWhitelist() {
+        Properties whitelistProperties = new Properties();
+        InputStream stream = null;
+        try {
+            stream =  LookAheadObjectInputStream.class.getResourceAsStream("resource-serialization.properties");
+            whitelistProperties.load(stream);
+        } catch (IOException e) {
+            throw new RuntimeException("Error loading the ResourceBuilder.properties file", e);
+        } finally {
+            if (stream != null) {
+                try {
+                    stream.close();
+                } catch (IOException e) {
+                    throw new RuntimeException("Error closing the ResourceBuilder.properties file", e);
+                }
+            }
+        }
+        for (String baseClassName : whitelistProperties.getProperty("whitelist").split(",")) {
+            try {
+                Class<?> baseClass = Class.forName(baseClassName);
+                whitelistBaseClasses.add(baseClass);
+            } catch (ClassNotFoundException e) {
+                throw new RuntimeException("Unable to load whiteList class " + baseClassName, e);
+            }
+        }
+    }
+}
diff --git a/framework/impl/src/main/java/org/ajax4jsf/resource/ResourceBuilderImpl.java b/framework/impl/src/main/java/org/ajax4jsf/resource/ResourceBuilderImpl.java
index d665af9..bfad526 100644
--- a/framework/impl/src/main/java/org/ajax4jsf/resource/ResourceBuilderImpl.java
+++ b/framework/impl/src/main/java/org/ajax4jsf/resource/ResourceBuilderImpl.java
@@ -372,12 +372,11 @@ public class ResourceBuilderImpl extends InternetResourceBuilder {
 			}
 			if ("B".equals(matcher.group(1))) {
 				data = objectArray;
 			} else {
 				try {
-					ObjectInputStream in = new ObjectInputStream(
-							new ByteArrayInputStream(objectArray));
+					ObjectInputStream in = new LookAheadObjectInputStream(new ByteArrayInputStream(objectArray));
 					data = in.readObject();
 				} catch (StreamCorruptedException e) {
 					log.error(Messages
 							.getMessage(Messages.STREAM_CORRUPTED_ERROR), e);
 				} catch (IOException e) {
diff --git a/framework/impl/src/main/java/org/ajax4jsf/resource/SerializableResource.java b/framework/impl/src/main/java/org/ajax4jsf/resource/SerializableResource.java
new file mode 100644
index 0000000..cd3cf0b
--- /dev/null
+++ b/framework/impl/src/main/java/org/ajax4jsf/resource/SerializableResource.java
@@ -0,0 +1,31 @@
+/**
+ * JBoss, Home of Professional Open Source
+ * Copyright 2010, Red Hat, Inc. and individual contributors
+ * by the @authors tag. See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ **/
+package org.ajax4jsf.resource;
+
+/**
+ * A marker interface, used to indicate that the class implementing this interfaces is cleared for deserialisation by
+ * the LookAheadObjectInputStream
+ *
+ * @author <a href="http://community.jboss.org/people/bleathem">Brian Leathem</a>
+ */
+public interface SerializableResource extends java.io.Serializable {
+}
diff --git a/framework/impl/src/main/java/org/ajax4jsf/resource/UserResource.java b/framework/impl/src/main/java/org/ajax4jsf/resource/UserResource.java
index 446f05d..eaccf5d 100644
--- a/framework/impl/src/main/java/org/ajax4jsf/resource/UserResource.java
+++ b/framework/impl/src/main/java/org/ajax4jsf/resource/UserResource.java
@@ -154,11 +154,11 @@ public class UserResource extends InternetResourceBase {
 	public boolean requireFacesContext() {
 		// TODO Auto-generated method stub
 		return true;
 	}
 	
-	public static class UriData implements Serializable {
+	public static class UriData implements SerializableResource {
 
 		/**
 		 * 
 		 */
 		private static final long serialVersionUID = 1258987L;
diff --git a/framework/impl/src/main/resources/org/ajax4jsf/resource/resource-serialization.properties b/framework/impl/src/main/resources/org/ajax4jsf/resource/resource-serialization.properties
new file mode 100644
index 0000000..8ad4878
--- /dev/null
+++ b/framework/impl/src/main/resources/org/ajax4jsf/resource/resource-serialization.properties
@@ -0,0 +1 @@
+whitelist = org.ajax4jsf.resource.InternetResource,org.ajax4jsf.resource.SerializableResource,javax.el.Expression,javax.faces.el.MethodBinding,javax.faces.component.StateHolderSaver,java.awt.Color
\ No newline at end of file
diff --git a/samples/richfaces-demo/src/main/java/org/richfaces/demo/media/MediaData.java b/samples/richfaces-demo/src/main/java/org/richfaces/demo/media/MediaData.java
index f3741fa..e7f4508 100644
--- a/samples/richfaces-demo/src/main/java/org/richfaces/demo/media/MediaData.java
+++ b/samples/richfaces-demo/src/main/java/org/richfaces/demo/media/MediaData.java
@@ -1,11 +1,12 @@
 package org.richfaces.demo.media;
 
 import java.awt.Color;
-import java.io.Serializable;
 
-public class MediaData implements Serializable{
+import org.ajax4jsf.resource.SerializableResource;
+
+public class MediaData implements SerializableResource {
 
 	private static final long serialVersionUID = 1L;
 	Integer Width=110;
 	Integer Height=50;
 	Color Background=new Color(0,0,0);
diff --git a/samples/richfaces-demo/src/main/java/org/richfaces/demo/paint2d/PaintData.java b/samples/richfaces-demo/src/main/java/org/richfaces/demo/paint2d/PaintData.java
index c554811..66293e5 100644
--- a/samples/richfaces-demo/src/main/java/org/richfaces/demo/paint2d/PaintData.java
+++ b/samples/richfaces-demo/src/main/java/org/richfaces/demo/paint2d/PaintData.java
@@ -1,10 +1,10 @@
 package org.richfaces.demo.paint2d;
 
-import java.io.Serializable;
+import org.ajax4jsf.resource.SerializableResource;
 
-public class PaintData implements Serializable{
+public class PaintData implements SerializableResource {
 	/**
 	 * 
 	 */
 	private static final long serialVersionUID = 1L;
 	String text;
diff --git a/ui/paint2D/src/main/java/org/richfaces/renderkit/html/Paint2DResource.java b/ui/paint2D/src/main/java/org/richfaces/renderkit/html/Paint2DResource.java
index 385d3d1..8abb680 100644
--- a/ui/paint2D/src/main/java/org/richfaces/renderkit/html/Paint2DResource.java
+++ b/ui/paint2D/src/main/java/org/richfaces/renderkit/html/Paint2DResource.java
@@ -25,11 +25,10 @@ package org.richfaces.renderkit.html;
 import java.awt.Color;
 import java.awt.Dimension;
 import java.awt.Graphics2D;
 import java.awt.image.BufferedImage;
 import java.io.IOException;
-import java.io.Serializable;
 
 import javax.faces.FacesException;
 import javax.faces.component.UIComponentBase;
 import javax.faces.context.FacesContext;
 import javax.faces.el.MethodBinding;
@@ -39,10 +38,11 @@ import org.ajax4jsf.resource.ImageRenderer;
 import org.ajax4jsf.resource.InternetResourceBase;
 import org.ajax4jsf.resource.JpegRenderer;
 import org.ajax4jsf.resource.PngRenderer;
 import org.ajax4jsf.resource.ResourceContext;
 import org.ajax4jsf.resource.ResourceRenderer;
+import org.ajax4jsf.resource.SerializableResource;
 import org.ajax4jsf.util.HtmlColor;
 import org.richfaces.component.UIPaint2D;
 
 /**
  * Resource for create image by managed bean method
@@ -124,11 +124,11 @@ public class Paint2DResource extends InternetResourceBase {
 		} else {
 			throw new FacesException("Data for painting image resource not instance of UIPaint2D");
 		}
 	}
 
-	private static final class ImageData implements Serializable {
+	private static final class ImageData implements SerializableResource {
 
 		private static final long serialVersionUID = 4452040100045367728L;
 		
 		int _width=1;
 		int _height = 1;

Vulnerable code: 					ObjectInputStream in = new ObjectInputStream(
							new ByteArrayInputStream(objectArray));
	public static class UriData implements Serializable {
import java.io.Serializable;
public class MediaData implements Serializable{
import java.io.Serializable;
public class PaintData implements Serializable{
import java.io.Serializable;
	private static final class ImageData implements Serializable {
Fixed code: /**
 * JBoss, Home of Professional Open Source
 * Copyright 2010, Red Hat, Inc. and individual contributors
 * by the @authors tag. See the copyright.txt in the distribution for a
 * full listing of individual contributors.
 *
 * This is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 2.1 of
 * the License, or (at your option) any later version.
 *
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this software; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 **/
package org.ajax4jsf.resource;

import java.io.IOException;
import java.io.InputStream;
import java.io.InvalidClassException;
import java.io.ObjectInputStream;
import java.io.ObjectStreamClass;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

/**
 * When deserializing objects, first check that the class being deserialized is in the allowed whitelist.
 *
 * @author <a href="http://community.jboss.org/people/bleathem">Brian Leathem</a>
 */
public class LookAheadObjectInputStream extends ObjectInputStream {
    private static final Map<String, Class<?>> PRIMITIVE_TYPES = new HashMap<String, Class<?>>(9, 1.0F);
    private static Set<Class> whitelistBaseClasses = new HashSet<Class>();
    private static Set<String> whitelistClassNameCache = Collections.newSetFromMap(new ConcurrentHashMap<String, Boolean>());

    static {
        PRIMITIVE_TYPES.put("bool", Boolean.TYPE);
        PRIMITIVE_TYPES.put("byte", Byte.TYPE);
        PRIMITIVE_TYPES.put("char", Character.TYPE);
        PRIMITIVE_TYPES.put("short", Short.TYPE);
        PRIMITIVE_TYPES.put("int", Integer.TYPE);
        PRIMITIVE_TYPES.put("long", Long.TYPE);
        PRIMITIVE_TYPES.put("float", Float.TYPE);
        PRIMITIVE_TYPES.put("double", Double.TYPE);
        PRIMITIVE_TYPES.put("void", Void.TYPE);

        whitelistClassNameCache.add(new Object[0].getClass().getName());
        whitelistClassNameCache.add(new String[0].getClass().getName());
        whitelistClassNameCache.add(new Boolean[0].getClass().getName());
        whitelistClassNameCache.add(new Byte[0].getClass().getName());
        whitelistClassNameCache.add(new Character[0].getClass().getName());
        whitelistClassNameCache.add(new Short[0].getClass().getName());
        whitelistClassNameCache.add(new Integer[0].getClass().getName());
        whitelistClassNameCache.add(new Long[0].getClass().getName());
        whitelistClassNameCache.add(new Float[0].getClass().getName());
        whitelistClassNameCache.add(new Double[0].getClass().getName());
        whitelistClassNameCache.add(new Void[0].getClass().getName());

        whitelistBaseClasses.add(String.class);
        whitelistBaseClasses.add(Boolean.class);
        whitelistBaseClasses.add(Byte.class);
        whitelistBaseClasses.add(Character.class);
        whitelistBaseClasses.add(Number.class);

        loadWhitelist();
    }

    public LookAheadObjectInputStream(InputStream in) throws IOException {
        super(in);
    }

    /**
     * Only deserialize primitive or whitelisted classes
     */
    @Override
    protected Class<?> resolveClass(ObjectStreamClass desc) throws IOException, ClassNotFoundException {
        Class<?> primitiveType = PRIMITIVE_TYPES.get(desc.getName());
        if (primitiveType != null) {
            return primitiveType;
        }
        if (!isClassValid(desc.getName())) {
            throw new InvalidClassException("Unauthorized deserialization attempt", desc.getName());
        }
        return super.resolveClass(desc);
    }

    /**
     * Determine if the given requestedClassName is allowed by the whitelist
     */
    boolean isClassValid(String requestedClassName) {
        if (whitelistClassNameCache.contains(requestedClassName)) {
            return true;
        }
        try {
            Class<?> requestedClass = Class.forName(requestedClassName);
            for (Class baseClass : whitelistBaseClasses ) {
                if (baseClass.isAssignableFrom(requestedClass)) {
                    whitelistClassNameCache.add(requestedClassName);
                    return true;
                }
            }
        } catch (ClassNotFoundException e) {
            return false;
        }
        return false;
    }

    /**
     * Load the whitelist from the properties file
     */
    static void loadWhitelist() {
        Properties whitelistProperties = new Properties();
        InputStream stream = null;
        try {
            stream =  LookAheadObjectInputStream.class.getResourceAsStream("resource-serialization.properties");
            whitelistProperties.load(stream);
        } catch (IOException e) {
            throw new RuntimeException("Error loading the ResourceBuilder.properties file", e);
        } finally {
            if (stream != null) {
                try {
                    stream.close();
                } catch (IOException e) {
                    throw new RuntimeException("Error closing the ResourceBuilder.properties file", e);
                }
            }
        }
        for (String baseClassName : whitelistProperties.getProperty("whitelist").split(",")) {
            try {
                Class<?> baseClass = Class.forName(baseClassName);
                whitelistBaseClasses.add(baseClass);
            } catch (ClassNotFoundException e) {
                throw new RuntimeException("Unable to load whiteList class " + baseClassName, e);
            }
        }
    }
}
					ObjectInputStream in = new LookAheadObjectInputStream(new ByteArrayInputStream(objectArray));
/**
 * JBoss, Home of Professional Open Source
 * Copyright 2010, Red Hat, Inc. and individual contributors
 * by the @authors tag. See the copyright.txt in the distribution for a
 * full listing of individual contributors.
 *
 * This is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 2.1 of
 * the License, or (at your option) any later version.
 *
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this software; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 **/
package org.ajax4jsf.resource;

/**
 * A marker interface, used to indicate that the class implementing this interfaces is cleared for deserialisation by
 * the LookAheadObjectInputStream
 *
 * @author <a href="http://community.jboss.org/people/bleathem">Brian Leathem</a>
 */
public interface SerializableResource extends java.io.Serializable {
}
	public static class UriData implements SerializableResource {
whitelist = org.ajax4jsf.resource.InternetResource,org.ajax4jsf.resource.SerializableResource,javax.el.Expression,javax.faces.el.MethodBinding,javax.faces.component.StateHolderSaver,java.awt.Color
import org.ajax4jsf.resource.SerializableResource;

public class MediaData implements SerializableResource {
import org.ajax4jsf.resource.SerializableResource;
public class PaintData implements SerializableResource {
import org.ajax4jsf.resource.SerializableResource;
	private static final class ImageData implements SerializableResource {