# Trestle has Server-Side Template Injection (SSTI) via Recursive Template Re-evaluation of Untrusted Data

Trestle's md_clean_include/mdsection_include Jinja2 tags re-parse untrusted Markdown as template source in an unsandboxed Environment, enabling SSTI to RCE.

- Payout: Research
- Severity: high
- Program: compliance-trestle
- Found by: EclipsSec
- Disclosed: 2026-08-28
- CVE: CVE-2026-54757
- Write-up quality: 7/10
- Original: https://github.com/advisories/GHSA-jw39-3688-r4rx
- Library page: https://bugbountylibrary.com/r/ghsa%3AGHSA-jw39-3688-r4rx
- Tags: area:oss domain:oss impact:rce kind:research platform:ghsa sev:high tech:python technique:incomplete-fix technique:second-order technique:source-review vuln:rce vuln:ssti

**Target:** Jinja2 custom tags (MDCleanInclude/MDSectionInclude) in trestle CLI
**Root cause:** Markdown file content and extracted sections are passed to jinja2.Parser().parse() as template source inside a non-sandboxed Environment, so data is executed as Jinja2 code.
**Steps:**
1. `trestle init`
2. Create `malicious.md` containing `{{ ssp.__class__.__init__.__globals__.__builtins__.__import__('os').popen('whoami').read() }}`
3. Create `trigger.md.jinja` containing `{% md_clean_include "malicious.md" %}`
4. Run `trestle author jinja -i trigger.md.jinja -o output.md -lut empty.yaml`
5. Observe command output (e.g. `root`) rendered into output.md
**How found:** Reporter noticed a systemic anti-pattern: runtime data (rendered output, included Markdown, LUT values) fed back into Parser.parse() without SandboxedEnvironment; traced the previously-fixed render_template while-loop to remaining custom tags in tags.py.
**Exploitation:** Attacker plants a malicious .md in the trestle workspace (or injects Jinja2 payload into SSP/LUT data fields); when a trusted template invokes md_clean_include/mdsection_include, the content is re-parsed and executed, yielding arbitrary command execution via __globals__/os.system or subprocess.
**Escalation:** Object-chain traversal (__class__.__mro__, __globals__, __subclasses__) escalates template expression evaluation to full Python RCE; CI/CD pipelines processing vendor SSPs widen blast radius.
**Insight:** When auditing Jinja2 integrations, grep for Parser( and Environment( — any custom tag that re-parses loaded file content or extracted sections is a second-order SSTI sink even if the main render path was already patched.

