OpenMed Privacy-Filter Model Loader Remote Code Execution via Substring-Matched trust_remote_code
Unauthenticated model_name substring bypass routes to Hugging Face trust_remote_code=True loading, yielding arbitrary Python execution on the OpenMed PII service
OpenMed, an open-source local-first healthcare AI toolkit for clinical NER and HIPAA PII de-identification, is vulnerable to remote code execution in versions before 1.5.2. Its privacy-filter model loader used naive substring matching on the client-supplied model_name parameter and defaulted to trust_remote_code=True, so any Hugging Face repo name merely containing "privacy-filter" (e.g. attacker/foo-privacy-filter-bar) would be downloaded and its embedded Python code executed with the service's own privileges — no authentication required. Blast radius includes full host compromise and exposure of any PHI/PII the service processes. Fixed in OpenMed 1.5.2 via an explicit model allowlist.
Technical Breakdown
OpenMed Privacy-Filter Model Loader Remote Code Execution via Substring-Matched trust_remote_code
OpenMed's privacy-filter dispatcher selects its Hugging Face model-loading code path using naive substring matching on the attacker-controlled model_name parameter — any identifier containing "privacy-filter" (e.g. attacker/foo-privacy-filter-bar) qualifies, regardless of namespace. That path additionally sets trust_remote_code=True by default in PrivacyFilterTorchPipeline, so a crafted attacker-owned Hugging Face repo with custom code declared via auto_map in config.json/tokenizer_config.json gets imported and executed with the OpenMed service process's privileges. No authentication is required to reach the vulnerable parameter.
No official pre-patch fix — firewall-block /pii/extract and /pii/deidentify, or enforce an exact-match model_name allowlist at the proxy layer
Root Cause
The dispatcher that routes a client-supplied model_name to a Hugging Face loader used a substring check ("privacy-filter" in model_name) instead of an exact match against the small, known set of legitimate privacy-filter model identifiers. Any Hugging Face repo path an attacker chooses to name — including their own public namespace — satisfies that check as long as the literal string appears anywhere in it.
Vulnerable Pattern
PrivacyFilterTorchPipeline additionally defaults trust_remote_code=True when instantiating the Transformers model. Combined with the substring bypass, this means the attacker doesn't just get OpenMed to download an unexpected model — they get it to import and execute arbitrary attacker-authored Python declared via auto_map, with zero code review or sandboxing between the download and the exec.
Why AI Services Are High-Value
OpenMed exists specifically to de-identify HIPAA PII — the service is trusted with the exact data a breach is supposed to protect. Model-loading pipelines in AI services routinely pull from public hubs by design, which is precisely the trust boundary this bug abuses; the same substring-matching-plus-trust_remote_code pattern recurs across ML frameworks that dynamically resolve "trusted" model families by name.
The Fix
OpenMed 1.5.2 replaces the substring check with an explicit allowlist of exact model identifiers (openai/privacy-filter, OpenMed/privacy-filter-multilingual, OpenMed/privacy-filter-nemotron) and flips trust_remote_code to default False, requiring an explicit opt-in for any future remote-code model loading.
Attack Chain
Recon — Identify the PII endpoint
Attacker fingerprints an internet- or intranet-facing OpenMed deployment and confirms it accepts a client-controlled model_name parameter for selecting the privacy-filter model.
Defensive note: Inventory and log all externally reachable AI/ML inference endpoints; alert on unauthenticated access to endpoints accepting model-identifier strings.
Weaponize — Publish a malicious Hugging Face repository
Attacker creates a public HF repo (e.g. attacker/foo-privacy-filter-bar) whose config.json/tokenizer_config.json declares an auto_map pointing to attacker-authored Python classes that execute arbitrary code on import.
Defensive note: Maintain an internal deny-list/review process for any HF repo referenced by production systems; alert on outbound pulls to newly-created or low-reputation HF namespaces.
Deliver — Submit crafted model_name to the API
Attacker sends an unauthenticated request to /pii/extract (or /deidentify) with model_name=attacker/foo-privacy-filter-bar; the substring "privacy-filter" satisfies the dispatcher's routing check.
Defensive note: Log/alert on model_name values that don't exactly match the allowlisted set; WAF/reverse-proxy rule rejecting non-canonical model identifiers before they reach the app.
Exploit — Untrusted code execution via trust_remote_code=True
The routed code path loads the model with trust_remote_code=True, causing Transformers to import and execute the attacker's custom code, running with the OpenMed service process's OS-level privileges.
Defensive note: Egress-filter the host so it cannot reach huggingface.co (or route through an allowlisting proxy); run inference workloads in a sandboxed/no-network container with minimal filesystem and no credential access.
Impact — Full compromise / PHI exposure
With arbitrary code execution, the attacker can exfiltrate any PHI/PII currently being processed, pivot laterally, install persistence, or pull additional payloads, all under the service account's privileges.
Defensive note: Monitor for anomalous outbound connections, unexpected child processes spawned by the inference service, and unusual file/model-cache writes; enforce least-privilege service accounts.
Impact
Unauthenticated RCE in a service explicitly trusted with de-identifying protected health data turns a single unpatched instance into both a data breach and a foothold for further compromise — with zero user interaction and zero credentials required.
Protected Health Information (PHI) / Data Confidentiality
- OpenMed is explicitly a HIPAA PII de-identification tool, so the data it's trusted to protect is exposed by its own compromise
- RCE grants read access to patient records/transcripts in memory or on disk during processing
- Attacker can exfiltrate model cache, logs, or upstream data-store credentials
- A breach here is a textbook HIPAA reportable incident
Host / Infrastructure Integrity
- Code executes with the OpenMed service process's OS privileges
- Potential for persistence via cron jobs, SSH keys, or backdoored packages
- Lateral movement into adjacent healthcare systems if the host isn't segmented
- Supply-chain risk if the model cache/path is reused elsewhere
Availability / Service Trust
- Arbitrary code execution can crash, ransomware, or disable the PII de-identification pipeline, halting downstream clinical workflows
- Once exploited, every prior de-identification result from that instance should be considered suspect
- No-auth + no-user-interaction makes mass-scanning against public instances trivial
- Recovery requires full instance rebuild, not just a config change
Defensive Tutorial
Patch to OpenMed 1.5.2
Immediatepip install --upgrade openmed>=1.5.2 — replaces substring routing with an explicit allowlist and flips trust_remote_code to default False. The only complete fix.
Audit logs for exploitation attempts
ImmediateGrep for model_name values containing "privacy-filter" that don't exactly match the three legitimate model identifiers (openai/privacy-filter, OpenMed/privacy-filter-multilingual, OpenMed/privacy-filter-nemotron).
Check the HF model cache for unexpected repos
ImmediateInspect ~/.cache/huggingface/hub (or HF_HOME) on affected hosts for any downloaded repo not on the allowlist; treat any match as confirmed compromise and rebuild the host.
Rotate credentials accessible to the service process
ImmediateAny API keys, DB credentials, or cloud IAM tokens reachable from the OpenMed process environment, given the RCE window.
Egress-filter Hugging Face access
ImportantRestrict outbound network access to only the allowlisted repo paths, or pre-stage approved models and block huggingface.co entirely.
Add a gateway-layer allowlist for model_name
ImportantEnforce an exact-match allowlist for the model_name parameter via a WAF/proxy rule, independent of application-level fixes.
Run inference workloads with least privilege
ImportantUse a dedicated non-root service account or sandboxed container with no access to PHI stores beyond what's strictly required.
SBOM and dependency alerting for AI/ML model-loading libraries
ImportantTrack openmed and its Transformers/HF dependency chain with alerts on trust_remote_code-adjacent CVEs; this substring-matching bug class recurs across ML frameworks that dynamically load "trusted" model families by name.
Policy: never default trust_remote_code=True on user-influenced input
ImportantCodify a secure-coding standard requiring exact-match allowlists (not substring/prefix checks) before enabling remote code execution flags, with mandatory security review for any new trust_remote_code=True usage.
References
- NVD Entry CVE-2026-47117 — National Vulnerability Database record with CVSS v4.0 and v3.1 scoring.
- GitHub Security Advisory GHSA-m3v4-v5gx-7wf5 — Advisory detail, credited to researcher faketut.
- Vendor Fix PR maziyarpanahi/openmed#59 — Patch introducing the explicit model allowlist and default-False trust_remote_code.
- Vendor Release OpenMed v1.5.2 — Release notes for the fixed version.
- CWE Reference CWE-94 — Improper Control of Generation of Code ('Code Injection')