Vulnerability of Pickle
As Artificial Intelligence and Machine Learning (AI/ML) become increasingly relevant in various aspects of life, business processes and across several industries, the attack surface is expanding, necessitating the need for us to address the visible and obscure threats.
Pickles are codes and should be treated with security in mind because they can execute malicious code.
Python pickle module supports a process called pickling (a Python-specific way) of storing objects. This process implements binary protocols for serializing and deserializing Python object structures. Pickle enables machine learning engineers to exchange pre-trained models rather than training from scratch. Unfortunately, as with traditional software supply-chain attacks, threat actors can poison and distribute malicious models. The most common strategy for achieving remote code execution is to tamper with the model Deserialization process.
This blog will examine the Pickle supply chain vulnerability in AI/ML pipelines with actionable recommendations on how to mitigate and detect exploitation. But first, let’s begin with the method of exploitation and some background.
What Is Serialization?
Serialization is the process of taking a variable (complex hierarchical data structure ) in a system’s memory and converting it into a string (linear form) that can be stored in a database, transmitted across a network, or placed in some location outside of the computer memory and loaded back up when needed. Like pickle, JSON is also a Python module for serialization, where a Java object is converted into a string that represents the values in the object. JSON is in a human-readable format, whereas pickle is in binary.
Serialization can be used in many different situations. One of the most common uses is saving the state of a neural network (a method in AI that is designed to mimic the behavior of the human brain) after the training phase, allowing for later use without having to redo the training. Pickles are like an archiving system used to save AI models.
Everything in variable x is converted to the linear form lined in red. Note, this a basic/simplified example.
Deserialization
The reverse process, which takes a stream of bytes and converts (loads) it back into a data structure. In other words, it reconstructs the original object from that byte stream. So here, everything is variable x, is back to its original form.
pickle Module - In PyTorch
PyTorch is an open-source machine learning library used for AI implementation, which heavily relies on serialization through the pickle module. Pickle includes functionality that makes it vulnerable to code execution and deserialization attacks. Pickle can run functions, import modules, or execute system commands if embedded.
Often, multiple pickles are stacked in one file to represent a model. PyTorch serialized pretrained models usually come as .pkl or .pt files (but could be in other formats). Note that pickle is an instruction type not a file type, so NumPy might have pickle or not.
Understanding pickle
A pickle can be compared with macros in an Office document. A document can contain embedded scripts (macros) that run when the file is opened, or a specific action is taken. In comparison, a malicious payload can be executed when a victim loads (deserializes) a malicious pickle by calling the functions that the threat actor instructed the serialized pickle to call. This could then grant the attacker a shell on the compromised machine, enabling them to gain full control over the victim’s machine through what is commonly referred to as a “backdoor.” Once code execution is established via the ‘backdoor,’ attackers can perform further recon, move laterally, pull malware or scripts, setup persistence mechanisms, exfiltrate data, essentially whatever they need to reach their objectives.
You can think of the stream as a tiny program that describes:
- Which Python objects/classes to reference
- What constructor or functions to call
- What arguments to pass
- How to restore the object’s internal state
The Python pickle module consists of four methods:
- pickle.dump(obj, file, protocol=None, *, fix_imports=True, buffer_callback=None)
- pickle.dumps(obj, protocol=None, *, fix_imports=True, buffer_callback=None)
- pickle.load(file, *, fix_imports=True, encoding="ASCII", errors="strict", buffers=None)
- pickle.loads(bytes_object, *, fix_imports=True, encoding="ASCII", errors="strict", buffers=None)
The first two methods are used during the pickling (serialization) process, and the latter two are used during the unpickling (Deserialization) process. The only difference between dump() and dumps() is that the first creates a file containing the serialization result, while the second returns a string.
Exploitation of Pickle
pickle stores both data and the metadata which are the instructions for how to rebuild that data, including calls to arbitrary Python functions.
When pickle.load() runs, Python executes those instructions to recreate the object, this gives way for the code embedded inside the pickle stream actually runs. If an attacker controls the pickle bytes, they can encode sequences that cause arbitrary callable to be invoked during unpickling.
Al feature, called _reduce_ (which is a method in python that serves a s a function), allows developers to define custom reconstruction logic, that tells Python which function to run and with what arguments when the object is deserialized.
Attackers abuse this by replacing harmless reconstruction logic with something malicious.
Trojanised Artifacts
This threat is not hypothetical, Reversing Labs threat researchers identified malicious Pickle files on Hugging Face in early 2025. The attack works when a poisoned model file is uploaded to a public repo or artifact store (which is a core component in MLflow Tracking, where MLflow stores (typically large) artifacts for each run, such as model weights (e.g. a pickled scikit-learn model), images (e.g. PNGs)). An unsuspecting user who downloads and pickle.load() the file might run malicious code embedded in it. The hype around AI projects and business application makes pretrained models and community artifacts an attractive supply-chain vector.
Dependency Compromise
A CI (Continuous Integration) pipeline, artifact repo, or build process that lacks secure code review and integrity checks could be altered so that legitimate artifacts are replaced with malicious pickles, leading to automatic compromise when those artifacts are loaded in downstream systems.
Chaining Post-Exploitation
Once code is executed in the target environment, attackers may perform lateral movement, read credentials, exfiltrate data, or install backdoors.
The New Reality
Machine Learning (ML) pipelines are powerful tools in today’s world; still, every pipeline carries potential security risks, ranging from untrusted data, bias, issues with AI explainability, and maliciously pre-trained models.
In ML workflows, serialization and Deserialization happen for several reasons and in various situations.
- Save checkpoints (modeldata.pkl, modeldata.pt)
- Exchange pretrained models
- Cache processed datasets
- Share configuration and preprocessing artifacts
ML Supply Chain Concerns
- ML systems are highly dependent on third-party artifacts:
- Pretrained models (for transfer learning)
- Feature engineering scripts and pipeline components
- Shared notebooks and serialized preprocessing artifacts
- ‘black box’ excuse for not properly scrutinizing dependencies
Each item that flows through your pipeline is a supply-chain dependency. Without provenance, integrity checking, and visibility, your organization may run code supplied by unknown or bad actors.
Defending Against a Malicious Pickle
Below are some ways you could defend against malicious pickle.
- Deserializing untrusted bytes is equivalent to executing untrusted code. Treat Deserialization as a high-risk operation.
- Use safer serialization formats: Prefer formats that only represent data structures (JSON, protobuf, ONNX for models, model state_dict() rather than full pickles). For PyTorch, save model.state_dict() and reconstruct the architecture in code, then call model.load_state_dict(...). This separates code (model class) from weights (numeric arrays).
- Verify cryptographic integrity: Store and verify hashes for every artifact. Refuse to accept models/datasets whose computed hash differs from the value in your AIBOM (if you have one, which is highly recommended).
- Use signed artifacts and secure registries: Host models in artifact repositories with access controls and signing. Prefer internal registries or verified vendor feeds over anonymous public downloads.
- Sandbox Deserialization: If Deserialization must accept external data
- Run it in a constrained environment like a dedicated VM or container with minimal privileges
- No mounts of sensitive volumes/credentials
- Network egress restrictions and strict logging, and monitoring
- Prefer static formats for runtime deployment
- Harden CI/CD
- Validate artifacts in CI using their AIBOM entries
- Run automated model scanning and behavior checks
- Prevent direct pickle.load() in production code paths without gating
- Logging & monitoring
- Detect suspicious activity: unexpected Deserialization operations, file access to model stores, and unusual outbound connections after model loading
- Threat modeling for Deserialization risk: a checklist
- When threat modeling your pipeline, include asset models, datasets, preprocessing code, and training infrastructure
- RCE in training infrastructure
Detection
Detecting pickle is not the easiest as it does not have any magic byte that can detect it as a pickle, not everything is a .pkl. PyTorch introduced features like the "weights-only unpickler" to prevent arbitrary code execution by only allowing certain functions to be called. The file generated by torch.save cannot be loaded using torch.load when weights_only=True. Static scanners (Semgrep) can flag unsafe usage (pickle.load, torch.load, etc.) in codebases (does not mean it is malicious, so this is quite broad).
Runtime monitoring tools (EDR, Sysmon, XDR (through process, CMD argument monitoring and correlation)) may detect indicators, e.g., unexpected process spawns (like os.system or subprocess during Deserialization) if sufficient logging is enabled.
However, it is important to note that the byte stream itself does not look malicious; it is valid pickle data until executed. So traditional signature-based detection often misses it.
Behavioral analytics (e.g., detecting unexpected network calls or file writes immediately after unpickling) can help identify attempts at exploitation.
What NEXT?
Serialization with pickle is powerful and convenient; however, it is also a significant supply-chain risk which can scale exponentially. The right security-first posture combines engineering discipline (using safe serialization, export weights instead of full pickles), operational controls (AIBOM, signed artifacts, integrity checks), Threat modeling (explicitly treating Deserialization as an execution boundary), and Runtime defences (sandboxing, monitoring).
Every artifact you load combines code and data. Treat it with the same skepticism and guardrails you apply to executable software. In enterprise pipelines, the better defense is prevention and process hardening. Never unpickle models from untrusted sources; use safer formats such as JSON and others (with a safe loader), or ONNX for model serialization.
Threat model your ML/AI components and pipeline.
The most important cultural change is to raise security awareness across the organization and remember that every time you load a pickle, you are executing someone else’s code and there is much more to pickle.