CVE-2013-4521 (published 2020-02-06T16:15Z)

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.

Severity: HIGH
CVSS v2 base score: 7.5 (AV:N/AC:L/Au:N/C:P/I:P/A:P)
CVSS v3 base score: 9.8, CRITICAL (CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H)

Weakness information:
cwe_id: CWE-502
cwe_name: Deserialization of Untrusted Data
description: The product deserializes untrusted data without sufficiently verifying that the resulting data will be valid.
extended_description: 
            It is often convenient to serialize objects for communication or to save them for later use. However, deserialized data or code can often be modified without using the provided accessor functions if it does not use cryptography to protect itself. Furthermore, any cryptography would still be client-side security -- which is a dangerous security assumption.
            Data that is untrusted can not be trusted to be well-formed.
            When developers place no restrictions on "gadget chains," or series of instances and method invocations that can self-execute during the deserialization process (i.e., before the object is returned to the caller), it is sometimes possible for attackers to leverage them to perform unauthorized actions, like generating a shell.
         
         

Fix information:
repo_url: https://github.com/nuxeo/richfaces
commit_details: repo_url: https://github.com/nuxeo/richfaces
author: Guillaume Renard
author_date: 2013-11-07 11:46:52+01:00
msg: NXBT-661: apply patch for CVE-2013-4521 flaw
repository: repo_url: https://github.com/nuxeo/richfaces
repo_name: nuxeo/richfaces-3.3
description: Fork of the richfaces 3.3.1.GA sources for patches needed by Nuxeo
repo_language: Java
owner: nuxeo
file_changes: filename: LookAheadObjectInputStream.java
change_type: ModificationType.ADD
diff: @@ -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);
+            }
+        }
+    }
+}

programming_language: Java
filename: ResourceBuilderImpl.java
change_type: ModificationType.MODIFY
diff: @@ -374,8 +374,7 @@ public class ResourceBuilderImpl extends InternetResourceBuilder {
 				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

programming_language: Java
filename: SerializableResource.java
change_type: ModificationType.ADD
diff: @@ -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 {
+}

programming_language: Java
filename: UserResource.java
change_type: ModificationType.MODIFY
diff: @@ -156,7 +156,7 @@ public class UserResource extends InternetResourceBase {
 		return true;
 	}
 	
-	public static class UriData implements Serializable {
+	public static class UriData implements SerializableResource {
 
 		/**
 		 * 

programming_language: Java
filename: resource-serialization.properties
change_type: ModificationType.ADD
diff: @@ -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

programming_language: Batchfile
filename: MediaData.java
change_type: ModificationType.MODIFY
diff: @@ -1,9 +1,10 @@
 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;

programming_language: Java
filename: PaintData.java
change_type: ModificationType.MODIFY
diff: @@ -1,8 +1,8 @@
 package org.richfaces.demo.paint2d;
 
-import java.io.Serializable;
+import org.ajax4jsf.resource.SerializableResource;
 
-public class PaintData implements Serializable{
+public class PaintData implements SerializableResource {
 	/**
 	 * 
 	 */

programming_language: Java
filename: Paint2DResource.java
change_type: ModificationType.MODIFY
diff: @@ -27,7 +27,6 @@ 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;
@@ -41,6 +40,7 @@ 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;
 
@@ -126,7 +126,7 @@ public class Paint2DResource extends InternetResourceBase {
 		}
 	}
 
-	private static final class ImageData implements Serializable {
+	private static final class ImageData implements SerializableResource {
 
 		private static final long serialVersionUID = 4452040100045367728L;
 		

programming_language: Java