Automated Threat Modeling

Brief Summary: CVE-2021-47952 Remote Code Execution in Python jsonpickle via py/repr Deserialization

A short review of CVE-2021-47952, a critical remote code execution vulnerability in Python's jsonpickle library version 2.0.0 that allows arbitrary command execution through malicious py/repr directives during JSON deserialization.

CVE Analysis

7 min read

ZeroPath CVE Analysis
ZeroPath CVE Analysis

2026-05-16

Brief Summary: CVE-2021-47952 Remote Code Execution in Python jsonpickle via py/repr Deserialization
Experimental AI-Generated Content

This CVE analysis is an experimental publication that is completely AI-generated. The content may contain errors or inaccuracies and is subject to change as more information becomes available. We are continuously refining our process.

If you have feedback, questions, or notice any errors, please reach out to us.

[email protected]

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:

  1. jsonpickle.decode() receives a JSON string and begins restoring Python objects.
  2. When the unpickler hits a py/repr tag in the JSON structure, it routes processing to the _restore_repr function.
  3. _restore_repr passes the tag's value to the loadrepr function.
  4. loadrepr splits the input string and calls Python's built in eval() 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:

  1. Find an application endpoint or data ingestion point that passes external input to jsonpickle.decode().
  2. Craft a JSON payload with a py/repr directive that names a Python module and a function call to execute.
  3. Submit the payload to the target application.
  4. When jsonpickle.decode() processes the payload, the loadrepr function evaluates the attacker's string through eval(), 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 eval and disable support for pre 0.7.0 repr serialized 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

Detect & fix
what others miss

Works with
  • GitHub
  • GitLab
  • Bitbucket
  • Azure DevOps Services
  • Gerrit
  • CVS
  • Jira
  • Linear
  • Slack
  • Security Compass
Explore integrations
Security magnifying glass visualization