CVE-2026-24123
Path traversal in BentoML's bentofile.yaml exfiltrates files on build.
- CVSS 6.5
- CWE-22
- Input Validation and Sanitization
- Local
BentoML is a Python library for building online serving systems optimized for AI apps and model inference. Prior to version 1.4.34, BentoML's `bentofile.yaml` configuration allows path traversal attacks through multiple file path fields (`description`, `docker.setup_script`, `docker.dockerfile_template`, `conda.environment_yml`). An attacker can craft a malicious bentofile that, when built by a victim, exfiltrates arbitrary files from the filesystem into the bento archive. This enables supply chain attacks where sensitive files (SSH keys, credentials, environment variables) are silently embedded in bentos and exposed when pushed to registries or deployed. Version 1.4.34 contains a patch for the issue.
- CWE
- CWE-22
- CVSS base score
- 6.5
- Published
- 2026-01-26
- OWASP
- A08 Software and Data Integrity Failures
- Orthogonal defect classification
- Build/Package/Merge
- Code defect classification
- Missing Check
- Category
- Input Validation and Sanitization
- Subcategory
- Path Traversal
- Accessibility scope
- Local
- Impact
- Data Theft
- Affected component
- BentoML
- Fixed by upgrading
- Yes
Solution
Upgrade to BentoML version 1.4.34 or later.
Vulnerable code sample
import os
import tempfile
import shutil
def vulnerable_file_inclusion(base_dir, user_path):
"""
This function represents the vulnerable logic in BentoML before the fix for
CVE-2026-24123. It constructs a file path by joining a base project
directory with a user-provided path from a configuration file like
'bentofile.yaml'. The lack of path sanitization allows an attacker to
read files outside of the intended base directory.
"""
# VULNERABLE STEP: os.path.join does not prevent directory traversal.
# An input like '../../../../etc/passwd' will be resolved to a path
# outside of 'base_dir'.
maliciously_constructed_path = os.path.join(base_dir, user_path)
try:
# The application reads the file, assuming it's within the project.
# This is where the file exfiltration occurs.
with open(maliciously_constructed_path, 'r', encoding='utf-8', errors='ignore') as f:
file_content = f.read()
return file_content
except FileNotFoundError:
return f"Error: The file '{maliciously_constructed_path}' was not found."
except Exception as e:
return f"An error occurred: {e}"
# --- Demonstration of the vulnerability ---
if __name__ == "__main__":
# 1. Create a fake project directory. The attack will attempt to escape this sandbox.
fake_project_dir = tempfile.mkdtemp(prefix="bentoml_project_")
print(f"[*] Created temporary project directory: {fake_project_dir}")
# 2. Define the attacker's payload. This path would be inside a malicious 'bentofile.yaml'.
# This payload targets '/etc/passwd' on Linux/macOS.
# A Windows equivalent could be '../../../../windows/win.ini'.
malicious_path_payload = '../../../../../../etc/passwd'
print(f"[*] Attacker's malicious path payload: '{malicious_path_payload}'")
# 3. Run the vulnerable function, simulating the BentoML build process.
print("\n[!] Executing the vulnerable function...")
exfiltrated_data = vulnerable_file_inclusion(fake_project_dir, malicious_path_payload)
# 4. Display the exfiltrated data to confirm the vulnerability.
print("-" * 50)
print("[+] VULNERABILITY SUCCESSFUL. Exfiltrated file content:")
print("-" * 50)
# Print the first 1000 characters of the exfiltrated file
print(exfiltrated_data[:1000])
print("-" * 50)
# 5. Clean up the temporary directory.
shutil.rmtree(fake_project_dir)
print(f"\n[*] Cleaned up directory: {fake_project_dir}")Patched code sample
import os
import sys
def get_validated_path(base_dir: str, user_path: str) -> str:
"""
Prevents path traversal attacks by ensuring a user-provided path is
safely contained within a trusted base directory.
This function represents the core logic used to fix the path traversal
vulnerability (similar to CVE-2024-24123) where fields in a
'bentofile.yaml' could be manipulated to exfiltrate arbitrary files.
It works by resolving both the base directory and the user-provided
path to their absolute, canonical forms and then verifying that the base
directory is a common parent of the resulting path.
Args:
base_dir: The absolute path to the trusted root directory (e.g., the
BentoML project build context).
user_path: The untrusted path provided by a user or configuration file
(e.g., from a 'description' or 'docker.setup_script' field).
Returns:
The resolved, absolute path if it is safe and within the base_dir.
Raises:
ValueError: If the path is determined to be unsafe (i.e., it
traverses outside the `base_dir`).
"""
# 1. Resolve the trusted base directory to its real, absolute path to
# prevent symbolic link manipulation and normalize its representation.
resolved_base_dir = os.path.realpath(base_dir)
# 2. Join the base directory with the user path and resolve it to its
# real, absolute path. This collapses any '..' components.
candidate_path = os.path.join(resolved_base_dir, user_path)
resolved_candidate_path = os.path.realpath(candidate_path)
# 3. Use `os.path.commonpath` to find the longest common sub-path.
# If the user path is safe, the common path will be the base directory.
# This is a robust method for detecting traversal attacks.
# On Windows, drive letters can differ in case. For the commonpath check to
# work reliably, we normalize the case before comparison.
if sys.platform == "win32":
common_prefix = os.path.commonpath(
[resolved_base_dir.lower(), resolved_candidate_path.lower()]
)
if common_prefix != resolved_base_dir.lower():
raise ValueError(
f"Path traversal detected. The path '{user_path}' resolves to "
f"'{resolved_candidate_path}', which is outside the "
f"allowed directory '{resolved_base_dir}'."
)
else:
# On POSIX systems, the comparison is case-sensitive and straightforward.
common_prefix = os.path.commonpath([resolved_base_dir, resolved_candidate_path])
if common_prefix != resolved_base_dir:
raise ValueError(
f"Path traversal detected. The path '{user_path}' resolves to "
f"'{resolved_candidate_path}', which is outside the "
f"allowed directory '{resolved_base_dir}'."
)
return resolved_candidate_pathPayload
service: "service:svc"
description: "file:../../../../../../../../etc/passwd"
labels:
owner: bentoml-team
stage: dev
include:
- "*.py"
python:
packages:
- scikit-learn
- pandas
Cite this entry
@misc{vaitp:cve202624123,
title = {{Path traversal in BentoML's bentofile.yaml exfiltrates files on build.}},
author = {Bogaerts, Fr\'ed\'eric and Ivaki, Naghmeh and Fonseca, Jos\'e},
year = {2026},
note = {VAITP Python Vulnerability Dataset, entry CVE-2026-24123},
howpublished = {\url{https://netpack.pt/vaitp/vulnerability/CVE-2026-24123/}}
}
Introducing the "VAITP dataset": a specialized repository of Python vulnerabilities and patches, meticulously compiled for the use of the security research community. As Python's prominence grows, understanding and addressing potential security vulnerabilities become crucial. Crafted by and for the cybersecurity community, this dataset offers a valuable resource for researchers, analysts, and developers to analyze and mitigate the security risks associated with Python. Through the comprehensive exploration of vulnerabilities and corresponding patches, the VAITP dataset fosters a safer and more resilient Python ecosystem, encouraging collaborative advancements in programming security.
The supreme art of war is to subdue the enemy without fighting.
Sun Tzu – “The Art of War”
:: Shaping the future through research and ingenuity ::
