FIX Mitigate Jinja2 Server-Side Template Injection (SSTI) vulnerability#1577
Conversation
Use SandboxedEnvironment instead of unsandboxed Environment/Template in
seed.py to block Python object traversal attacks (e.g. __class__.__mro__).
Add {% raw %}...{% endraw %} wrapping to 21 remote dataset loaders that
were passing fetched data directly into SeedPrompt/SeedObjective value
parameters without Jinja2 escaping. This prevents template injection
from poisoned remote datasets.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: adrian-gavrila <50029937+adrian-gavrila@users.noreply.github.com>
Verify that SandboxedEnvironment blocks Python object traversal
when a malicious payload uses {% endraw %} to escape a {% raw %}
wrapper. Tests both render_template_value and render_template_value_silent.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: adrian-gavrila <50029937+adrian-gavrila@users.noreply.github.com>
Replace remote fetch from GitHub URL with bundled JSON file to eliminate supply chain risk. The dataset (400 examples from KutalVolkan/many-shot-jailbreaking-dataset@5eac855) is now loaded from pyrit/datasets/jailbreak/many_shot_examples.json. Rename fetch_many_shot_jailbreaking_dataset to load_many_shot_jailbreaking_dataset and remove requests dependency. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: adrian-gavrila <50029937+adrian-gavrila@users.noreply.github.com>
Replace inline f-string raw wrapping across 31 remote dataset loaders with a shared escape_jinja_template_syntax() function in remote_dataset_loader.py. This makes the pattern discoverable and harder to forget when adding new loaders. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: adrian-gavrila <50029937+adrian-gavrila@users.noreply.github.com>
…ES_PATH The vendored JSON file is at datasets/jailbreak/, not datasets/jailbreak/templates/ — it's data, not a template. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: adrian-gavrila <50029937+adrian-gavrila@users.noreply.github.com>
47cad95 to
6b97a8b
Compare
58b23aa to
ab55b31
Compare
ab55b31 to
3db7148
Compare
Add jinja_template field to Seed (default False). When False, __post_init__ automatically wraps the value in raw tags to prevent template injection. Trusted sources (from_yaml_file) set jinja_template=True to allow Jinja2 rendering. This eliminates the need for callers to remember escape_jinja_template_syntax(). Dataset loaders no longer need explicit escaping — SeedPrompt(value=remote_data) is safe by default. Propagation: SeedGroup and SeedDataset from_yaml_file overrides pass jinja_template=True through to nested seed construction. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: adrian-gavrila <50029937+adrian-gavrila@users.noreply.github.com>
3db7148 to
befa47f
Compare
…ty (#1577) Co-authored-by: Roman Lutz <romanlutz@users.noreply.github.com> Co-authored-by: adrian-gavrila <50029937+adrian-gavrila@users.noreply.github.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
Hey genuine question. I reported this exact vuln to MSRC on April 5 (VULN-181209 / Case 112392) unsandboxed Jinja2 in SeedPrompt.post_init |
|
Hi @cherry-bisht ! Thanks for reaching out. I can check with MSRC. Mind sending me whatever you submitted to MSRC and their response to romanlutz@microsoft.com? I can respond here or via email with whatever I find. Regardless of what we find here, thank you for submitting your finding to MSRC and trying to keep people safe! |
Problem
PyRIT's template rendering in
seed.pyused an unsandboxed Jinja2Environment, and 21 of 30 remote dataset loaders passed fetched data directly intoSeedPrompt(value=...)without escaping. SinceSeedPrompt.__post_init__rendersself.valueas a Jinja2 template, a poisoned remote dataset could achieve Python object traversal (e.g."".__class__.__mro__[1].__subclasses__()) — confirmed viaproof-of-concept.
The 9 loaders that did wrap values in
{% raw %}...{% endraw %}were also bypassable via{% endraw %}injection in the payload.Changes
Layer 1 — Sandbox the rendering engine (
seed.py)Environment()/Template()withSandboxedEnvironmentfromjinja2.sandbox__class__,__mro__,__subclasses__()and other unsafe attribute accessrender_template_value/render_template_value_silentLayer 2 — Safe-by-default
SeedPromptis_jinja_templatefield toSeed(defaultFalse)False,__post_init__auto-wraps the value in{% raw %}...{% endraw %}before renderingSeed.from_yaml_fileandSeedDataset.from_yaml_filesetis_jinja_template=Truefor trusted local YAML templatesSeedPrompt(value=remote_data)is safe by defaultSeedPromptwith Jinja2 template syntax explicitly opt in withis_jinja_template=TrueLayer 3 — Eliminate supply chain risk in many-shot jailbreak
pyrit/datasets/jailbreak/many_shot_examples.json. The dataset was provided by @KutalVolkan originally. For simplicity, we'reincluding it here as well.
requests.get()from GitHub URL with localjson.load()fetch_many_shot_jailbreaking_dataset→load_many_shot_jailbreaking_datasetfetch_many_shot_jailbreaking_dataset(removal in 0.14.0)Regression tests
test_untrusted_seed_prompt_auto_escapes_template_syntax— verifies default auto-escapingtest_render_template_value_blocks_ssti_via_endraw_injection— verifies sandbox blocks{% endraw %}escape attack on trusted templatestest_render_template_value_silent_blocks_ssti_via_endraw_injection— same for the silent rendering pathtest_seed_group_untrusted_auto_escapes— verifies SeedGroup propagates untrusted defaultTesting
SecurityErrorwith sandbox, executes without