Introduction
A single call to jsonpickle.decode() on untrusted input is all it takes to hand an attacker full code execution on your server. CVE-2021-47952 documents a critical remote code execution vulnerability in the Python jsonpickle library version 2.0.0, where the deserialization of py/repr tagged JSON objects passes attacker controlled strings directly into Python's eval() function.
jsonpickle is an open source Python library that extends standard JSON serialization to support complex Python object graphs. It is widely used in Python projects that need to serialize and reconstruct arbitrary objects, and it is available on PyPI with broad adoption across the Python ecosystem. Its ability to faithfully round trip Python objects through JSON makes it a popular choice, but that same capability is what makes it dangerous when exposed to untrusted data.
Technical Information
The root cause of CVE-2021-47952 lies in how jsonpickle's unpickler handles py/repr tagged objects during deserialization. Unlike standard JSON parsers that produce only basic data types, jsonpickle reconstructs full Python objects from JSON representations. This reconstruction process includes a code path that evaluates arbitrary expressions.
In version 2.0.0, the deserialization flow works as follows:
jsonpickle.decode()receives a JSON string and begins restoring Python objects.- When the unpickler encounters a
py/reprtag in the JSON structure, it routes processing to the_restore_reprfunction. _restore_reprpasses the tag's value to theloadreprfunction.loadreprsplits the input string and directly invokes Python's built ineval()function on the attacker supplied content.
The critical detail is that the safe keyword argument defaults to False in version 2.0.0, meaning eval() is permitted by default with no opt in required from the developer.
Attack Flow
An attacker exploiting this vulnerability would follow these steps:
- Identify an application endpoint or data ingestion point that passes external input to
jsonpickle.decode(). - Craft a JSON payload containing a
py/reprdirective that specifies a Python module and a function call to execute. - Submit the payload to the target application.
- When
jsonpickle.decode()processes the payload, theloadreprfunction evaluates the attacker's string viaeval(), executing arbitrary Python code with the privileges of the running process.
The following example from public exploit research demonstrates the payload structure:
malicious = '{"1": {"py/repr": "time/time.sleep(10)"}, "2": {"py/id": 67}}' some_parameter = jsonpickle.decode(malicious)
This payload causes the application to call time.sleep(10) during deserialization. The same mechanism can be used to invoke os.system() or any other Python function, enabling full system command execution on both Windows and Linux targets.
The attack requires no authentication, no special privileges, and no user interaction. Any application that deserializes external JSON input through jsonpickle is potentially vulnerable.
CWE Classification
This vulnerability is classified under CWE-94 (Improper Control of Generation of Code, also known as Code Injection), which accurately reflects the direct eval() invocation on attacker controlled input.
Affected Systems and Versions
The vulnerability affects jsonpickle version 2.0.0 and earlier versions that include the loadrepr function with eval() enabled by default. Any Python application that calls jsonpickle.decode() on data from an untrusted source while running an affected version is vulnerable.
Newer versions of jsonpickle have made changes to avoid using eval and to disable support for legacy repr serialized objects by default. However, relying solely on the safe=True flag is insufficient, as historical issues demonstrate that code execution can still occur through other mechanisms like py/object and py/reduce even when safe mode is enabled.
Vendor Security History
The jsonpickle project has a documented history of security concerns related to deserialization. The maintainers have consistently communicated that the library is not designed to be safe against malicious input, and multiple GitHub issues have been filed over the years requesting clearer security warnings and safer defaults.
Notable items from the project's security history include:
- GitHub issue #178 raised the concern that jsonpickle should more clearly warn about the security hazard of deserializing untrusted data.
- GitHub issue #332 documented that the
decode()function unsafely forms objects that can lead to code execution. - GitHub issue #335 flagged the broader deserialization of untrusted data concern.
- The project's changelog documents updates to the unpickler to avoid using
evaland to disable support for pre 0.7.0reprserialized objects by default in later releases.
The maintainers' position is that security is the responsibility of the caller. The official security policy states plainly that loading a JSON string from an untrusted source is always a potential security vulnerability.
References
- VulnCheck Advisory: python jsonpickle 2.0.0 Remote Code Execution via py/repr
- Exploit DB: python jsonpickle 2.0.0 Remote Code Execution
- jsonpickle GitHub Repository
- jsonpickle Official Documentation
- jsonpickle Security Policy
- GitHub Issue #332: jsonpickle decode() unsafely forms objects
- GitHub Issue #335: Security issue in this repo
- GitHub Issue #178: jsonpickle should more clearly warn about security hazard
- jsonpickle Version History
- CISA Known Exploited Vulnerabilities Catalog
- jsonpickle v2.0.0 unpickler.py source



