From d75a6eaa4164d3fc0ac67bc35048b021b85e9b6f Mon Sep 17 00:00:00 2001 From: Stypox Date: Sun, 31 Dec 2023 18:21:48 +0100 Subject: [PATCH] Fix vulnerability with whitelist-aware ObjectInputStream Only a few specific classes are now allowed. --- .../settings/export/ImportExportManager.kt | 5 +- .../export/PreferencesObjectInputStream.java | 58 +++++++++++++++++++ 2 files changed, 61 insertions(+), 2 deletions(-) create mode 100644 app/src/main/java/org/schabi/newpipe/settings/export/PreferencesObjectInputStream.java diff --git a/app/src/main/java/org/schabi/newpipe/settings/export/ImportExportManager.kt b/app/src/main/java/org/schabi/newpipe/settings/export/ImportExportManager.kt index 9954a2b36..298841c5f 100644 --- a/app/src/main/java/org/schabi/newpipe/settings/export/ImportExportManager.kt +++ b/app/src/main/java/org/schabi/newpipe/settings/export/ImportExportManager.kt @@ -8,7 +8,6 @@ import org.schabi.newpipe.streams.io.SharpOutputStream import org.schabi.newpipe.streams.io.StoredFileHelper import org.schabi.newpipe.util.ZipHelper import java.io.IOException -import java.io.ObjectInputStream import java.io.ObjectOutputStream import java.util.zip.ZipOutputStream @@ -78,7 +77,9 @@ class ImportExportManager(private val fileLocator: NewPipeFileLocator) { try { val preferenceEditor = preferences.edit() - ObjectInputStream(fileLocator.settings.inputStream()).use { input -> + PreferencesObjectInputStream( + fileLocator.settings.inputStream() + ).use { input -> preferenceEditor.clear() @Suppress("UNCHECKED_CAST") val entries = input.readObject() as Map diff --git a/app/src/main/java/org/schabi/newpipe/settings/export/PreferencesObjectInputStream.java b/app/src/main/java/org/schabi/newpipe/settings/export/PreferencesObjectInputStream.java new file mode 100644 index 000000000..0d11b0b61 --- /dev/null +++ b/app/src/main/java/org/schabi/newpipe/settings/export/PreferencesObjectInputStream.java @@ -0,0 +1,58 @@ +package org.schabi.newpipe.settings.export; + +import java.io.IOException; +import java.io.InputStream; +import java.io.ObjectInputStream; +import java.io.ObjectStreamClass; +import java.util.Set; + +/** + * An {@link ObjectInputStream} that only allows preferences-related types to be deserialized, to + * prevent injections. The only allowed types are: all primitive types, all boxed primitive types, + * null, strings. HashMap, HashSet and arrays of previously defined types are also allowed. Sources: + * + * cmu.edu + * , + * + * OWASP cheatsheet + * , + * + * Apache's {@code ValidatingObjectInputStream} + * + */ +public class PreferencesObjectInputStream extends ObjectInputStream { + + /** + * Primitive types, strings and other built-in types do not pass through resolveClass() but + * instead have a custom encoding; see + * + * official docs. + */ + private static final Set CLASS_WHITELIST = Set.of( + "java.lang.Boolean", + "java.lang.Byte", + "java.lang.Character", + "java.lang.Short", + "java.lang.Integer", + "java.lang.Long", + "java.lang.Float", + "java.lang.Double", + "java.lang.Void", + "java.util.HashMap", + "java.util.HashSet" + ); + + public PreferencesObjectInputStream(final InputStream in) throws IOException { + super(in); + } + + @Override + protected Class resolveClass(final ObjectStreamClass desc) + throws ClassNotFoundException, IOException { + if (CLASS_WHITELIST.contains(desc.getName())) { + return super.resolveClass(desc); + } else { + throw new ClassNotFoundException("Class not allowed: " + desc.getName()); + } + } +}