Duration: 2 hr lecture + 4 hr lab + 5 hr independent Lab: Lab 8 (CVE-2025-65106 Reproduction: LangChain SSTI) OWASP anchor: LLM03:2025 Supply Chain + LLM05:2025 Improper Output Handling (root cause spans both) CVE: CVE-2025-65106, GHSA-6qv9-48xg-fc7f Affected versions: langchain-core <= 1.0.6, <= 0.3.79 Fixed versions: langchain-core >= 1.0.7, >= 0.3.80
8.1 What This Module Is
Module 8 is a single-CVE deep dive. Rather than surveying vulnerability categories, we trace one real CVE from advisory to root cause to exploitation to patch. The goal is to develop the skill of reading a CVE and reasoning about it from first principles -- a skill you will use repeatedly in AI-101 and beyond.
CVE-2025-65106 is a template injection vulnerability in LangChain's PromptTemplate class. It was disclosed in late 2025, affects one of the most widely deployed LLM application frameworks, and maps cleanly to two OWASP LLM Top 10 entries.
8.2 Background: LangChain PromptTemplate
LangChain's PromptTemplate is the standard way to construct structured prompts from templates:
from langchain.prompts import PromptTemplate
template = PromptTemplate(
input_variables=["name", "topic"],
template="You are {name}. Answer this question about {topic}: ..."
)
prompt = template.format(name="Alice", topic="Python security")
LangChain supports three template formats: f-string (default Python format strings), mustache (handlebars-style {{variable}}), and jinja2 (full Jinja2 template engine).
The vulnerability exists in all three formats but through different mechanisms:
| Format | Vulnerability mechanism |
|---|---|
| f-string | {variable.attribute} allows attribute access on the variable object |
| Mustache | getattr() fallback allows attribute traversal beyond dict keys |
| Jinja2 | Insufficient sandboxing -- full Jinja2 sandbox environment was not applied |
8.3 The SSTI Primitive
Server-Side Template Injection (SSTI) occurs when user-supplied content is processed by a template engine as template code rather than as data. The key insight: template engines are programming languages. They have conditionals, loops, object access, and often access to the Python object hierarchy.
The classic Jinja2 SSTI gadget:
# In Jinja2 templates, you can traverse Python's object hierarchy:
{{ "".__class__.__mro__[1].__subclasses__() }}
# Returns all Python classes currently in scope
{{ config.__class__.__init__.__globals__['os'].popen('id').read() }}
# On a poorly sandboxed Jinja2 environment, executes OS commands
LangChain used Jinja2's Environment class for its Jinja2 template format but did not use SandboxedEnvironment, which blocks attribute and method access. The result: any Jinja2 template string supplied by a user or attacker could traverse Python object attributes and access internal state.
8.4 The Attack Vector
The vulnerability is exploitable when an application:
- Accepts user-supplied template strings (not just user-supplied template variables)
- Passes those strings to
PromptTemplatewithtemplate_format="jinja2"
Vulnerable code pattern:
from langchain.prompts import PromptTemplate
# Attacker controls the template string, not just the variables
user_template = request.json["template"] # DANGEROUS
prompt = PromptTemplate(
input_variables=["topic"],
template=user_template,
template_format="jinja2"
)
result = prompt.format(topic="AI security")
Attacker payload:
user_template = "{{ ''.__class__.__mro__[1].__subclasses__() }}"
# Or more specifically:
user_template = "{{ config }}" # Access LangChain's internal config object
In the f-string format, the analog is:
user_template = "{topic.__class__.__mro__}"
# Accesses the __mro__ attribute of the topic variable's class
8.5 What Data Is Exposed
In the LangChain context, successful SSTI can expose:
- The
topicvariable's Python class hierarchy (useful for understanding application structure) - LangChain internal configuration objects (may contain API keys, model parameters, memory contents)
- Environment variables via
os.environif the Jinja2 sandbox allowsosmodule access - The full Python object space if the sandbox is not applied at all
The GHSA advisory classifies this as "High" severity (CVSS 8.1) because:
- The attack requires no authentication (if the template endpoint is public)
- Exploitation is straightforward and well-documented (Jinja2 SSTI gadgets are in every web app security tool)
- Affected applications may store API keys or sensitive data in LangChain configuration objects
8.6 The Patch
LangChain fixed CVE-2025-65106 in versions 1.0.7 and 0.3.80 with three changes:
Fix 1: F-string format -- variable name validation
# Before: allowed {variable.attribute} and {variable[key]}
# After: validates that variable names are simple Python identifiers only
# Regex: re.match(r'^[a-zA-Z_][a-zA-Z0-9_]*$', var_name)
Fix 2: Mustache format -- type restriction
# Before: used getattr() fallback allowing object attribute traversal
# After: strict type checking; only dict, list, and tuple traversal allowed
# getattr() path removed entirely
Fix 3: Jinja2 format -- restricted sandbox
# Before: used Jinja2 Environment (no sandboxing)
# After: uses _RestrictedSandboxedEnvironment (custom class)
# _RestrictedSandboxedEnvironment blocks all attribute and method access
# This prevents the ''.__class__.__mro__... gadget chain entirely
Reading the patch diff is part of Lab 8. The diff shows exactly what changed in langchain_core/prompts/prompt.py.
8.7 OWASP Mapping
| Aspect | OWASP entry | Reasoning |
|---|---|---|
| Root cause: insufficient template sandboxing | LLM05:2025 Improper Output Handling | Template engine processes attacker-controlled content without sanitization |
| Distribution as part of LangChain supply chain | LLM03:2025 Supply Chain | Vulnerable version distributed via PyPI; applications pulling from supply chain were automatically affected |
| Potential credential exposure via config object | LLM07:2025 System Prompt Leakage | If config contains system prompt or API keys |
This CVE is unusual in that it spans three OWASP entries. The lab asks you to justify each mapping in writing.
8.8 Lab Preview: What You Will Do
Lab 8 is a complete CVE reproduction. You will:
-
Set up the vulnerable environment. Install
langchain-core==1.0.6in an isolated venv. Confirm the version. -
Reproduce the f-string vulnerability. Write a minimal Python script that creates a
PromptTemplateaccepting user-supplied template strings in f-string format. Craft a payload that accessestopic.__class__.__mro__and confirm the output exposes Python internals. -
Reproduce the Jinja2 vulnerability. Repeat with
template_format="jinja2". Craft the Jinja2 gadget that accesses the class hierarchy. Confirm the output. -
Apply the patch. Upgrade to
langchain-core==1.0.7. Confirm both reproduction scripts now raiseValueErroror produce sanitized output. -
Read the patch. Download and read the patch diff. Identify the three fix locations from Section 8.6 in the actual code.
-
Write the OWASP mapping. 1-2 paragraphs mapping the vulnerability to OWASP LLM03, LLM05, and optionally LLM07. This written component is graded separately.
8.9 Defender Lessons
Lesson 1: Template engines are code execution surfaces. Whenever a template engine (Jinja2, Mustache, Handlebars, ERB) processes user-supplied content, it is potentially executing user-supplied code. The only safe approach is to treat user input as template variables, never as template strings.
Lesson 2: Framework abstraction does not eliminate injection risk. LangChain is an abstraction layer over multiple LLM providers. Developers using LangChain may not realize they are using Jinja2 templates at all -- the template format is a parameter that defaults to f-string but can be changed. Abstraction layers can obscure injection surfaces.
Lesson 3: Sandboxing must be complete. LangChain used the Jinja2 library but did not use SandboxedEnvironment. Using a dangerous library with partial protections is often worse than not using it at all -- developers assume the library is handling safety when it is not.
Lesson 4: Check your versions. pip audit flags known vulnerabilities in installed packages. Running pip audit on a LangChain application pinned to 1.0.6 would have flagged CVE-2025-65106 immediately after it was published.
8.10 Module 8 Summary
| Concept | Key takeaway |
|---|---|
| SSTI in LangChain | User-controlled template strings processed by Jinja2 without sandboxing |
| Attack surface | Applications that accept user-supplied template strings (not just variables) |
| Exploitation | Standard Jinja2 SSTI gadget chain; no special knowledge required |
| Patch | F-string variable validation; Mustache type restriction; Jinja2 restricted sandbox |
| OWASP mapping | LLM05 (output handling) + LLM03 (supply chain); optional LLM07 |
| Defender rule | Never process user input as template code; only as template data |
Reading for Module 9
- OWASP LLM09:2025 (Misinformation) advisory
- OWASP LLM10:2025 (Unbounded Consumption) advisory
- EchoLeak paper: arXiv 2509.10540 (assigned reading before Module 9 lecture)
- CVE-2025-32711 NVD advisory