This is a receptionist who runs whatever package you hand over before checking whether you belong in the building
CVE-2026-45829 is a pre-auth remote code execution flaw in the ChromaDB Python FastAPI server. On chromadb versions >= 1.0.0 and <= 1.5.9, the create_collection flow builds an attacker-controlled embedding function before it performs the auth check. If the attacker sets kwargs.trust_remote_code=true and points model_name at a malicious Hugging Face-style repository, Chroma fetches and executes that code inside the server process. The Rust deployment path is not affected.
In a vacuum this looks like a straight CRITICAL: network-reachable, no auth, no user interaction, code exec. In real estates, the severity comes down one notch because the blast population is narrower than the raw bug suggests: you need the Python backend, not the Rust one; you need the API to be reachable from the attacker; and many enterprises do not expose their vector stores broadly. But if you *do* run exposed Python Chroma, the vendor-less score inflation debate is academic — it is still a serious server takeover path.
4 steps from start to impact.
Reach a Python Chroma API
POST /api/v2/tenants/{tenant}/databases/{db}/collections, which is documented as authenticated but processes the request body before the auth gate fires. Weaponized tool: a simple curl request or any HTTP client; no exploit kit required.- Target is running the Python FastAPI server, not the Rust path
- Attacker can reach the Chroma API port over network
- Affected package version is in the vulnerable range
- Many deployments are internal-only and never exposed to the internet
- Some self-hosted environments front Chroma with an API gateway, VPN, or private service mesh
- Organizations using the Rust deployment path are out of scope
/api/v2/heartbeat. Hadrian published a conservative nuclei fingerprint for the vulnerable behavior.Send a malicious embedding configuration
kwargs.trust_remote_code=true. Chroma passes those attacker-controlled settings into embedding-function builders, including SentenceTransformerEmbeddingFunction.build_from_config, which forwards kwargs into the model loader. Weaponized tool: a Hugging Face-style repo containing attacker Python modules.- Embedding-function config from the request is accepted
- The server environment has the relevant model-loading dependencies available
- Outbound access or local path access exists to retrieve the malicious model payload
- If the runtime cannot resolve or load the attacker model, exploitation can fail
- Some hardened hosts restrict outbound internet egress to model registries
- Not every Chroma deployment includes the same optional ML dependencies
trust_remote_code semantics.Exploit the auth-ordering flaw
load_create_collection_configuration_from_json() runs before sync_auth_request(). That means model code can execute *before* the request is rejected. HiddenLayer showed the request can return an error outwardly while the payload already ran inside the Chroma process. Weaponized tool: the transformers loading path behind AutoModel.from_pretrained() or equivalent upstream loaders.- The vulnerable code path is intact in the installed package
- The chosen embedding function forwards attacker kwargs into model loading
- No upstream wrapper blocks the dangerous fields before Chroma sees them
- A reverse proxy that enforces auth *before* the request reaches Chroma can break the chain
- Custom request validation or schema stripping at the edge may remove the exploit fields
- Some defensive egress controls stop the remote model fetch even though the app logic is vulnerable
401/403/500 after code already executed. EDR on the host is your best shot at seeing the spawned process, file write, or suspicious module import.Take over the server process
- The service account has access to valuable local secrets or adjacent services
- EDR or container confinement does not immediately stop post-exploitation behavior
- Least-privilege containers and locked-down service accounts can reduce lateral movement
- Read-only filesystems, seccomp, and egress controls can cap the blast radius
- Some AI stacks isolate vector stores from broader crown-jewel networks
The supporting signals.
| In-the-wild status | As of 2026-05-31, I found no CISA KEV entry and no authoritative public reporting of active exploitation campaigns. Treat it as *highly weaponizable*, not yet *proven at scale*. |
|---|---|
| Proof-of-concept availability | Yes. HiddenLayer published a working demonstration, the public GitHub issue documents the vulnerable call chain, and Hadrian published a nuclei fingerprint for exposure validation. |
| EPSS | 0.168% (38th percentile) in the GitHub Advisory view sourced from FIRST. That is low threat-frequency telemetry, not low impact. |
| KEV status | Not listed in the CISA KEV catalog as of 2026-05-31. |
| Third-party CVSS context | There is no vendor-maintainer severity baseline, which is why this is ASSESSED AT HIGH rather than upgraded or downgraded. Public third-party scoring exists: GitHub Reviewed shows 9.3 CVSS v4 and NVD displays a HiddenLayer CNA vector, but neither is a maintainer patch-priority statement. |
| CVSS vector interpretation | GitHub Reviewed lists CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:H/VA:H/SC:H/SI:H/SA:H/E:P, which matches the technical reality: network reachable, no auth, no user action, full server compromise. |
| Affected versions | The strongest public package metadata says chromadb >= 1.0.0 and <= 1.5.9 on the Python project path. HiddenLayer originally stated unpatched through 1.5.8; GitHub Advisory was later updated to include 1.5.9. |
| Fixed versions | None published in the GitHub Advisory as of 2026-05-31. No distro backport guidance or vendor-confirmed fixed Python release was found. The Rust deployment path is not affected, which matters operationally. |
| Scanning and exposure data | HiddenLayer reports 73% of internet-exposed ChromaDB instances discovered via Shodan were in the vulnerable version range. Hadrian's write-up shows a practical nuclei probe for confirming exposure without executing code. |
| Disclosure and researcher | Publicly disclosed 2026-05-18 by HiddenLayer; research article attributed to Esteban Tonglet. HiddenLayer says disclosure attempts started on 2026-02-17. |
noisgate verdict.
The decisive drag on this from CRITICAL to HIGH is population reachability, not exploit quality. This is a devastating bug on any exposed Python Chroma server, but it does not hit the Rust path and it only matters where attackers can actually reach the API, which narrows the real enterprise blast population.
Why this verdict
- Network-reachable pre-auth RCE: no credentials, no user interaction, and code runs before auth enforcement on the vulnerable Python path.
- Population narrowed by deployment choice: the Rust deployment path is not affected, so this is not a whole-product emergency across every Chroma install.
- Exposure is the make-or-break prerequisite: requiring HTTP reachability to the API means private-only or tightly segmented deployments are materially safer than internet-exposed ones.
- No vendor-maintainer fixed release as of 2026-05-31: lack of an official patch increases operational risk because mitigation has to carry the load.
- Low EPSS and no KEV: current exploitation telemetry is weak, which argues against a top-bucket CRITICAL fleet-wide panic score.
Why not higher?
I am not calling this CRITICAL fleet-wide because the real-world reachable population is smaller than the naked bug suggests. You need the vulnerable Python server path, not Rust, and you need attacker reachability to the API; those are meaningful friction points when you are triaging across 10,000 hosts rather than staring at a single exposed AI service.
Why not lower?
I am not dropping this to MEDIUM because once the prerequisites are met, the exploit chain is brutally short and the impact is full server compromise. Pre-auth code execution against an exposed service is still the kind of bug that becomes a breach, not a nuisance.
What to do — in priority order.
- Remove untrusted network reachability — Put every affected Python Chroma API behind a private network boundary, VPN, service mesh policy, or IP allowlist. For a HIGH verdict, deploy this within 30 days; this is the single most effective compensating control because the exploit starts with plain HTTP reachability.
- Front Chroma with an auth-enforcing reverse proxy — Terminate requests at a gateway that performs authentication and authorization *before* traffic reaches the Python FastAPI service. Deploy within 30 days and validate that unauthenticated requests never reach
/api/v2/tenants/.../collections. - Block model-registry egress from the service — Deny outbound access from Chroma containers/hosts to Hugging Face and other unapproved artifact sources, except through a vetted internal mirror if the workflow truly requires it. Deploy within 30 days; this directly breaks the remote-model fetch stage of the exploit.
- Prefer the Rust deployment path — If you must keep Chroma online before a vendor-confirmed Python fix exists, migrate exposed workloads to the Rust-based deployment path, which public research says is not affected. Treat this as a compensating architecture change and complete it within 30 days for internet-facing or broadly reachable services.
- Watch for suspicious collection creation and host behavior — Alert on POSTs to collection-creation endpoints, unexpected model loads, outbound fetches to model registries, and new process/file activity under the Chroma service account. Deploy within 30 days; this will not prevent exploitation, but it improves time-to-detect.
- Turning on Chroma's own auth alone does not solve this, because the vulnerable Python code path performs dangerous model loading before the auth check.
- A generic WAF is weak here unless it specifically understands and blocks the dangerous JSON fields; the exploit rides a normal API route with application-valid structure.
- MFA is irrelevant because the exploit does not need an account.
- Relying on HTTP status codes is dangerous; the request can fail outwardly while attacker code already executed.
Crowdsourced verification payload.
Run this on the target host, container, or image where chromadb is installed. Invoke it with python3 verify_cve_2026_45829.py using the same Python environment that runs Chroma; no admin rights are required, but you do need filesystem access to the installed package. The script performs a static package inspection and prints exactly one of VULNERABLE, PATCHED, or UNKNOWN.
#!/usr/bin/env python3
# verify_cve_2026_45829.py
# Static verifier for CVE-2026-45829 in the ChromaDB Python package.
# Exit codes: 0=PATCHED, 1=VULNERABLE, 2=UNKNOWN
import importlib
import importlib.util
import os
import re
import sys
from pathlib import Path
VULN_MIN = (1, 0, 0)
VULN_MAX = (1, 5, 9)
def parse_version(v):
if not v:
return None
nums = re.findall(r"\d+", str(v))
if not nums:
return None
parts = [int(x) for x in nums[:3]]
while len(parts) < 3:
parts.append(0)
return tuple(parts)
def read_text(p):
try:
return Path(p).read_text(encoding="utf-8", errors="ignore")
except Exception:
return ""
def main():
try:
chromadb = importlib.import_module("chromadb")
except Exception:
print("UNKNOWN")
sys.exit(2)
version = getattr(chromadb, "__version__", None)
vtuple = parse_version(version)
pkg_dir = Path(getattr(chromadb, "__file__", "")).resolve().parent
if not pkg_dir.exists():
print("UNKNOWN")
sys.exit(2)
fastapi_file = pkg_dir / "server" / "fastapi" / "__init__.py"
collcfg_file = pkg_dir / "api" / "collection_configuration.py"
sentxf_file = pkg_dir / "utils" / "embedding_functions" / "sentence_transformer_embedding_function.py"
fastapi_txt = read_text(fastapi_file)
collcfg_txt = read_text(collcfg_file)
sentxf_txt = read_text(sentxf_file)
# Heuristic 1: vulnerable ordering in create_collection path.
idx_load = fastapi_txt.find("load_create_collection_configuration_from_json")
idx_auth = fastapi_txt.find("sync_auth_request")
order_vuln = idx_load != -1 and idx_auth != -1 and idx_load < idx_auth
# Heuristic 2: attacker-controlled embedding config reaches known embedding functions.
cfg_vuln = (
'known_embedding_functions[ef_config["name"]]' in collcfg_txt and
'build_from_config(ef_config["config"])' in collcfg_txt
)
# Heuristic 3: sentence transformer forwards kwargs to constructor, enabling trust_remote_code flow.
kwargs_forwarded = (
'kwargs = config.get("kwargs", {})' in sentxf_txt and
'**kwargs' in sentxf_txt
)
# Heuristic 4: evidence of an obvious patch pattern.
auth_before_load = idx_auth != -1 and idx_load != -1 and idx_auth < idx_load
strips_kwargs = (
'pop("kwargs"' in fastapi_txt or
'del json_map["embedding_function"]["config"]["kwargs"]' in collcfg_txt or
'trust_remote_code' in collcfg_txt and 'False' in collcfg_txt
)
in_version_range = vtuple is not None and VULN_MIN <= vtuple <= VULN_MAX
if order_vuln and cfg_vuln and kwargs_forwarded and (in_version_range or version is None):
print("VULNERABLE")
sys.exit(1)
if auth_before_load or strips_kwargs:
print("PATCHED")
sys.exit(0)
# Version-only fallback: GitHub Advisory says <= 1.5.9 affected and patched versions are None.
if in_version_range:
print("VULNERABLE")
sys.exit(1)
print("UNKNOWN")
sys.exit(2)
if __name__ == "__main__":
main()
If you remember one thing.
chromadb release immediately once it exists.Sources
What defenders are saying.
Crowdsourced verification outputs.
Results submitted by users who ran the verification payload against their environment.