# LlamaIndex Pickle Index Deserialization to Code Execution

- **Severity:** High
- **Signature ID:** `7c4f1e2a-9b3d-4c6e-8f51-2a7d9e6b0c34`

## Summary

LlamaIndex versions up to 0.11.6 load saved index files using Python's pickle format without checking if they're safe. An attacker who can supply a malicious index file can get arbitrary commands run on the server the moment it's loaded.

## How the attack works

An attacker crafts a malicious pickle file (for example, disguised as multi_embed_store.pkl) containing a __reduce__ gadget, and places it where a victim application will load it via BGEM3Index.load_from_disk(). When the victim process reads that pickle file, Python begins deserializing it, and as part of that deserialization the gadget spawns a child process — a shell, curl/wget, netcat, base64 decode, or similar — to run attacker-chosen commands. The detection watches for this specific pairing: a Python process reading a pickle file under a persist/storage directory, followed within 45 seconds by that same process spawning a shell-like or download-like command.

## Why it matters

Successful exploitation gives an attacker arbitrary code execution on whatever host runs the vulnerable LlamaIndex loading code, with the privileges of that process — potentially full compromise of the application server.

## What you can do

- Upgrade LlamaIndex past the vulnerable version or avoid using BGEM3Index.load_from_disk() on files from untrusted or shared sources.
- Never load pickle-format index files that were not generated and stored by your own trusted pipeline.
- Restrict filesystem permissions on persist/storage directories so untrusted users cannot write or replace index files there.
- Monitor for Python processes that read .pkl/.pickle files and shortly afterward spawn shell, network, or scripting commands, and treat unexpected instances as suspicious.

## Known benign look-alikes

- Legitimate ML/RAG pipelines that load a cached pickled index and then immediately shell out (subprocess with shell=True) to git, dvc, aws, gsutil or a build tool. Mitigated by requiring the pickle path to be vuln-named (multi_embed_store / bgem3 / multi_embed) or to sit under a persist/storage/index_store directory before a mere "sh -c"/"bash -c" wrapper is treated as evidence.
- Jupyter/IPython notebooks that unpickle a model and then run a shell escape such as "!curl" or "!pip install" in the next cell. Mitigated by the 45 second ordered window and by report-only enforcement.
- Model or dataset download scripts that read a .pkl cache and then invoke curl/wget to fetch remaining shards. Mitigated by the persist-directory / vuln-name requirement plus the tight window; still reported by design because the sequence is indistinguishable from the exploit at the syscall layer.
- Security researchers or CI jobs intentionally reproducing this CVE against a sacrificial persist directory.
- Packaging, freeze or test steps (pytest, tox, cx_Freeze) that unpickle fixtures and then spawn compilers or chmod. Mitigated by the payload-shape allowlist-free tiering (compilers and package managers are not payload-shaped) and by report-only action.

## References

- https://cwe.mitre.org/data/definitions/502.html
- https://owasp.org/Top10/A08_2021-Software_and_Data_Integrity_Failures/
- https://docs.python.org/3/library/pickle.html#restricting-globals

---
Source: https://www.netzilo.com/threats/llamaindex-pickle-deserialization-rce
