Remote Code Execution via jsonpickle Deserialization

Critical Risk Deserialization & Object Security
pythonjsonpickledeserializationrcepicklecode-execution

What it is

jsonpickle deserialization vulnerabilities occur when jsonpickle.decode() processes untrusted data. Unlike standard JSON, jsonpickle preserves Python object types and can reconstruct complex objects including invoking constructors and __reduce__ methods. Attackers can craft malicious payloads that execute arbitrary code during deserialization.

import jsonpickle
from flask import Flask, request, jsonify

app = Flask(__name__)

# VULNERABLE: jsonpickle can execute arbitrary code
@app.route('/api/data/import', methods=['POST'])
def import_data():
    data = request.get_json()
    
    # DANGEROUS: jsonpickle.decode can execute code
    obj = jsonpickle.decode(data['payload'])
    
    if hasattr(obj, 'process'):
        result = obj.process()
        return jsonify({'status': 'success', 'result': result})
    
    return jsonify({'status': 'success', 'data': str(obj)})

# Malicious payload:
# {
#   "py/object": "__main__.MaliciousClass",
#   "py/reduce": [
#     {"py/function": "os.system"},
#     {"py/tuple": ["rm -rf /"]}
#   ]
# }
import json
from flask import Flask, request, jsonify
from pydantic import BaseModel, ValidationError

app = Flask(__name__)

# Define explicit data models
class UserData(BaseModel):
    name: str
    email: str
    preferences: dict
    
    class Config:
        extra = 'forbid'  # Reject unknown fields

# SECURE: use standard JSON with validation
@app.route('/api/data/import', methods=['POST'])
def import_data():
    try:
        data = request.get_json()
        
        # SAFE: standard JSON parsing
        raw_data = data['payload']
        
        # Validate against schema
        user_data = UserData(**raw_data)
        
        result = process_user_data(user_data)
        return jsonify({'status': 'success', 'result': result})
        
    except ValidationError as e:
        return jsonify({'error': 'Invalid data', 'details': e.errors()}), 400
    except Exception as e:
        return jsonify({'error': 'Processing failed'}), 500

def process_user_data(user_data: UserData) -> str:
    return f"Processed user: {user_data.name}"

💡 Why This Fix Works

The vulnerable code uses jsonpickle.decode() which can reconstruct arbitrary Python objects and execute code through constructors or __reduce__ methods. The secure version uses standard json parsing with Pydantic validation to ensure data conforms to expected schemas without object reconstruction.

Why it happens

Decoding JSON from user requests, external APIs, or uploaded files using jsonpickle.

Root causes

Using jsonpickle.decode() with Untrusted Data

Decoding JSON from user requests, external APIs, or uploaded files using jsonpickle.

Session Restoration with jsonpickle

Using jsonpickle to restore session or cache data that could be tampered with.

API Endpoints Accepting jsonpickle Payloads

Accepting jsonpickle-encoded data in API endpoints without validation.

Fixes

1

Use Standard JSON Module

Replace jsonpickle with json.loads() for parsing JSON data.

2

Define Explicit Schemas

Use Pydantic, marshmallow, or dataclasses to validate data against expected schemas.

3

Never Deserialize to Arbitrary Objects

Only deserialize to simple data structures (dicts, lists, strings, numbers).

Detect This Vulnerability in Your Code

Sourcery automatically identifies remote code execution via jsonpickle deserialization and many other security issues in your codebase.