Introduction
One call to jsonpickle.decode() on untrusted input is enough to hand an attacker full code execution on your server. CVE-2021-47952 is a critical remote code execution vulnerability in the Python jsonpickle library version 2.0.0, where deserializing py/repr tagged JSON objects passes attacker controlled strings straight into Python's eval() function.
jsonpickle is an open source Python library that extends standard JSON serialization to support complex Python object graphs. Python projects use it when they need to serialize and reconstruct arbitrary objects, and it is available on PyPI with broad adoption. Its ability to round trip Python objects through JSON is why people reach for it, and that same ability is what makes it dangerous when it meets untrusted data.
Technical Information
The root cause of CVE-2021-47952 is how jsonpickle's unpickler handles py/repr tagged objects during deserialization. A standard JSON parser produces only basic data types. jsonpickle instead reconstructs full Python objects from JSON, and one path in that reconstruction evaluates arbitrary expressions.
In version 2.0.0, the deserialization flow works like this:
jsonpickle.decode()receives a JSON string and begins restoring Python objects.- When the unpickler hits 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 calls Python's built ineval()directly on the attacker supplied content.
The critical detail is that the safe keyword argument defaults to False in version 2.0.0. So eval() runs by default, with no opt in required from the developer.
Attack Flow
An attacker exploiting this vulnerability would follow these steps:
- Find an application endpoint or data ingestion point that passes external input to
jsonpickle.decode(). - Craft a JSON payload with a
py/reprdirective that names 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 througheval(), running arbitrary Python code with the privileges of the running process.
The following example from public exploit research shows the payload structure:
malicious = '{"1": {"py/repr": "time/time.sleep(10)"}, "2": {"py/id": 67}}' some_parameter = jsonpickle.decode(malicious)
This payload makes the application call time.sleep(10) during deserialization. The same mechanism can invoke os.system() or any other Python function, which gives full system command execution on both Windows and Linux targets.
The attack needs 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 matches the direct eval() call 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 avoid eval and disable support for legacy repr serialized objects by default. But the safe=True flag alone is not enough: history shows that code execution can still occur through other mechanisms like py/object and py/reduce even in safe mode.
Vendor Security History
The jsonpickle project has a documented history of security concerns around deserialization. The maintainers have said consistently that the library is not designed to be safe against malicious input, and people have filed multiple GitHub issues over the years asking for clearer security warnings and safer defaults.
Notable items from the project's security history:
- GitHub issue #178 raised the concern that jsonpickle should warn more clearly about the 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 concern of deserializing untrusted data.
- The project's changelog records updates to the unpickler that avoid
evaland disable support for pre 0.7.0reprserialized objects by default in later releases.
The maintainers' position is that security is the caller's responsibility. 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



