CVE-2026-33155 · GHSA-54jj-px8x-5w5q · CWE-400 · HIGH 8.7 How we found a memory exhaustion vulnerability in DeepDiff that can crash a server with a payload smaller than a tweet - and why millions of Python applications are in its blast radius. DeepDiff is one of those libraries you probably haven't heard of - but your code almost certainly uses it. It's a Python utility for comparing objects: dictionaries, lists, data classes, nested structures. It's simple, useful, and downloaded 29 million times a month from PyPI. It is also present in Amazon-related tooling, meaning many teams may be exposed without realising it. Given its widespread adoption across the broader ML ecosystem, the potential blast radius of this vulnerability extends to thousands of applications and the organisations that rely on them. In 2025, a serious vulnerability was found in DeepDiff's serialisation mechanism: theDeltaclass could be exploited via malicious pickle payloads to achieve remote code execution and object pollution (CVE-2025-58367). The maintainers responded promptly - converting theSAFE_TO_IMPORTallowlist to afrozensetand hardening the_RestrictedUnpickleragainst class traversal. The intent was to make deserialising Delta objects from untrusted sources safe. During a supply chain audit of our own codebase at Periphery, we found that the patch left something behind. Our process started with a systematic review of third-party dependencies. We weren't hunting for anything specific - we were doing the kind of routine supply chain hygiene that, frankly, more teams should build into their development cycles. We scanned our imports, traced data flows to and from deserialisation boundaries, and started looking closely at anything that touched pickle. DeepDiff came up. We knew about the prior CVE. We pulled the patched version, read the fix, and noticed something. The restricted unpickler validateswhich classescan be loaded. It does not validatewhat is passed to their constructors. The allowlist -SAFE_TO_IMPORT- includes types likebuiltins.bytes,builtins.list, andbuiltins.range. These are permitted because they're considered safe from a code execution standpoint. They are. But they share a property that the patch didn't account for: their constructors allocate memory proportional to their input. Passbytes(10_000_000_000)and you get a 10 GB allocation. The restricted unpickler doesn't override theREDUCEopcode and doesn't inspect what's passed to constructors before they execute. We had a hypothesis. We built a payload to test it. Finding:A 42-byte pickle payload forces over 10 GB of memory allocation during deserialisation. The allocation happens before any application-level logic runs. The process crashes or is killed by the OS. The_RestrictedUnpickler.find_classmethod gates class loading against the allowlist. What it does not do is intercept theREDUCEopcode or inspect constructor arguments. This means any class on the allowlist can be instantiated with arbitrary arguments - including arguments that trigger unbounded memory allocation. The relevant check inserialization.py(line 353): def find_class(self, module, name):if f"{module}.{name}" not in SAFE_TO_IMPORT:raise UnpicklingError(f"Attempting to unpickle unsafe class: {module}.{name}")return super().find_class(module, name) The class is checked. The arguments passed to it are not. The door is locked but the window is open. The simplest attack. A pickle payload that callsbuiltins.bytes(N)with a very large integer. The allocation happens during deserialisation, before any delta processing begins. GLOBAL builtins.bytes # passes find_class — it's on the allowlistINT 10000000000 # 10 billionTUPLE + REDUCE # → bytes(10**10) → ~9.3 GB allocated instantly Payload size:42 bytes. Memory allocated:~9.3 GB. Amplification:~2,000,000×. A more subtle path, exploitable without raw pickle knowledge. It works by crafting a valid diff dictionary that first sets a value to a large integer viavalues_changed, then converts it to bytes viatype_changes. The ordering is key. Indelta.pyline 183,Delta.add()processes_do_values_changed()before_do_type_changes(). Step one writes a large integer into the target object in place. Step two reads that modified value and callsnew_type(current_old_value)at line 576 - with no size guard.
A memory exhaustion vulnerability (CVE-2026-33155, CVSS 8.7 HIGH) exists in the DeepDiff Python library where its patched restricted unpickler fails to validate constructor arguments for allowed classes. An attacker can send a small malicious pickle payload (e.g., 42 bytes) that triggers massive memory allocations (e.g., 10 GB), causing a denial-of-service crash. This widely used library is present in AWS, DataHub, and Lightning AI ecosystems, exposing numerous applications.