From aebadd2edf81762764c02d35a1a37c54d5849b81 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Sun, 28 Jun 2026 08:48:56 +0000 Subject: [PATCH] Fix insecure deserialization vulnerability Use ValidatingObjectInputStream from commons-io to enforce an allowlist of classes during deserialization, mitigating a potential Remote Code Execution (RCE) vulnerability. Co-authored-by: tomdesair <14034630+tomdesair@users.noreply.github.com> --- .jules/sentinel.md | 4 ++++ src/main/java/me/desair/tus/server/util/Utils.java | 6 ++++-- 2 files changed, 8 insertions(+), 2 deletions(-) create mode 100644 .jules/sentinel.md diff --git a/.jules/sentinel.md b/.jules/sentinel.md new file mode 100644 index 0000000..d4dba02 --- /dev/null +++ b/.jules/sentinel.md @@ -0,0 +1,4 @@ +## 2024-06-28 - Insecure Deserialization in UploadInfo parsing +**Vulnerability:** The application reads serialized UploadInfo files from disk using a standard `ObjectInputStream`. This allows an attacker to inject arbitrary serialized objects if they have the ability to write to the storage directory, potentially leading to Remote Code Execution (RCE) via insecure deserialization. +**Learning:** `ObjectInputStream` inherently deserializes any class that implements `Serializable` and is available on the classpath. Even when expecting a specific class like `UploadInfo`, the object stream will instantiate the malicious class before the cast occurs. +**Prevention:** Use `ValidatingObjectInputStream` from `commons-io` to enforce a strict allowlist of classes that can be deserialized. This ensures only expected classes (e.g., `UploadInfo` and its valid fields like `UploadType`, `UploadId`) are processed, rejecting unexpected classes before instantiation. diff --git a/src/main/java/me/desair/tus/server/util/Utils.java b/src/main/java/me/desair/tus/server/util/Utils.java index e72af32..cd1dde2 100644 --- a/src/main/java/me/desair/tus/server/util/Utils.java +++ b/src/main/java/me/desair/tus/server/util/Utils.java @@ -8,7 +8,6 @@ import jakarta.servlet.http.HttpServletRequest; import java.io.BufferedOutputStream; import java.io.IOException; -import java.io.ObjectInputStream; import java.io.ObjectOutput; import java.io.ObjectOutputStream; import java.io.OutputStream; @@ -22,6 +21,7 @@ import java.util.regex.Pattern; import me.desair.tus.server.HttpHeader; import me.desair.tus.server.checksum.ChecksumAlgorithm; +import org.apache.commons.io.serialization.ValidatingObjectInputStream; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -83,7 +83,9 @@ public static T readSerializable(Path path, Class clazz) throws IOExcepti // Lock will be released when the channel is closed if (lockFileShared(channel) != null) { - try (ObjectInputStream ois = new ObjectInputStream(Channels.newInputStream(channel))) { + try (ValidatingObjectInputStream ois = + new ValidatingObjectInputStream(Channels.newInputStream(channel))) { + ois.accept("java.lang.*", "java.util.*", "me.desair.tus.**", "[*"); info = clazz.cast(ois.readObject()); } catch (ClassNotFoundException | java.io.EOFException