High

SSTI Jinja2 Template Injection RCE (Haystack Pipeline)

This detects command-line evidence that someone exploited a server-side template injection flaw in deepset Haystack (versions before 2.3.1) to run arbitrary code. It looks for the tell-tale combination of Jinja2 template syntax and Python's sandbox-escape trick showing up together in a spawned process.

How the attack works

Haystack lets users build and run Pipelines that include components (PromptBuilder, OutputAdapter, ConditionalRouter) which render Jinja2 templates. An attacker submits a malicious template containing the classic Jinja2 sandbox-escape chain (__class__ -> __mro__ -> __subclasses__ -> __globals__/__import__) instead of normal input. When Haystack renders this template with the privileges of its own process, the payload re-enters a Python or shell interpreter, commonly via python -c, python -m, or sh -c, and the exploit text is carried verbatim into the resulting process command line. The detection fires only when Jinja2-style delimiters AND a dangerous Python construct both appear together in that command line — either alone is common in legitimate tooling.

Netzilo detection

Netzilo reports this behaviour when it is observed.

Signature ID
7c3f9a2e-5b41-4d6c-9e08-2a1f6b84c37d
Severity
High

Why it matters

A successful exploit runs attacker-chosen code with the same privileges as the Haystack service, which can lead to data theft, further lateral movement, or full host compromise depending on what that process can access.

What you can do

  • Upgrade Haystack to 2.3.1 or later, or restrict who can submit custom Pipeline definitions.
  • Do not let untrusted users control template strings fed into PromptBuilder, OutputAdapter, or ConditionalRouter.
  • Run the Haystack process with the least privilege necessary, so a template-injection RCE has limited blast radius.
  • Review any alerts for legitimate causes first — red-team SSTI testing, SAST scanners referencing gadget strings, or developers debugging Jinja2/CPython introspection can all trigger this pattern.

Known benign look-alikes

  • Security researchers or red teams executing SSTI proof-of-concept payloads against a local Haystack or Flask/Jinja2 instance — the command line is indistinguishable from a real exploit by design.
  • SAST/secret scanners invoked with SSTI gadget strings as rule or pattern arguments; filtered when the scanner is argv[0], NOT filtered when wrapped in `sh -c "semgrep ..."`.
  • Developer debugging of CPython introspection or Jinja2 sandbox behaviour, e.g. `python -c "print(().__class__.__mro__[1].__subclasses__())"` — only fires if an execution sink (os.system/subprocess/__import__) is also present in the same command line.
  • CTF tooling, exploit-development scratch scripts, and security-training exercises run on developer workstations.
  • Configuration management that renders Jinja braces around shell strings (ansible, consul-template, j2cli); filtered when the renderer is argv[0].
  • Template-engine unit tests whose fixture payload is passed on the command line rather than read from a file.

References