Executive Summary
CVE-2026-40477 is a critical Server-Side Template Injection (SSTI) security bypass in Thymeleaf, the popular Java server-side template engine widely used in Spring Boot applications. Due to an improper restriction of accessible objects within Thymeleaf's expression sandbox, unauthenticated remote attackers can bypass built-in protections and inject malicious expressions — leading to arbitrary code execution on the server. All applications running Thymeleaf 3.1.3.RELEASE or earlier that pass unvalidated user input to the template engine are at risk, and immediate patching to 3.1.4.RELEASE is required.
1. What Is This Vulnerability?
Thymeleaf is a Java template engine commonly embedded in Spring MVC and Spring Boot applications to render dynamic HTML. It provides a sandboxed expression evaluation mechanism intended to prevent template injection attacks — but CVE-2026-40477 reveals that sandbox is incomplete.
The vulnerability is rooted in two compounding weaknesses:
- CWE-917 – Expression Language Injection: Thymeleaf's expression evaluator does not properly neutralize user-controlled data before evaluating it as a template expression.
- CWE-1336 – Improper Neutralization in Template Engines: The built-in blocklist that should block access to sensitive Java objects is incomplete, and a whitespace parsing quirk allows certain payloads to bypass blocklist checks entirely.
Together, these two blind spots allow an attacker to step outside the sandbox and interact with Java runtime objects directly — leading to Server-Side Template Injection (SSTI), arbitrary file reads/writes, and full Remote Code Execution (RCE).
Attack Vector
The attack surface is any Thymeleaf-backed endpoint where user-supplied input (query parameters, form fields, path variables, headers) is passed directly to templateEngine.process() or rendered as a Thymeleaf template fragment without sanitization.
A simplified malicious payload might look like:
# URL parameter injected into a Thymeleaf template context
GET /search?query=__$%7BT(java.lang.Runtime).getRuntime().exec('id')%7D__
# Decoded payload
__${T(java.lang.Runtime).getRuntime().exec('id')}__
When Thymeleaf evaluates this input, the T() Spring Expression Language (SpEL) operator is invoked, resolving the Runtime class and executing an OS command. In CVE-2026-40477, specially crafted whitespace sequences and variant expression syntax allow the blocklist to be bypassed even when Thymeleaf's restricted mode is enabled.
Real-World Impact
Thymeleaf is one of the most widely deployed Java template engines — it is the default in Spring Boot's web starter and is used by hundreds of thousands of Java web applications globally. Exploitation of this CVE could allow attackers to:
- Execute arbitrary OS commands as the application server user
- Exfiltrate environment variables, credentials, and secrets
- Read or write arbitrary files on the server filesystem
- Pivot laterally within a compromised environment
- Deploy web shells or ransomware payloads
As of publication, no widespread active exploitation has been publicly confirmed, but proof-of-concept code has been referenced in security research circles.
2. Who Is Affected?
| Component | Affected Versions |
|---|---|
org.thymeleaf:thymeleaf |
≤ 3.1.3.RELEASE |
org.thymeleaf:thymeleaf-spring5 |
≤ 3.1.3.RELEASE |
org.thymeleaf:thymeleaf-spring6 |
≤ 3.1.3.RELEASE |
You are at risk if your application:
- Uses any Thymeleaf version 3.1.3.RELEASE or earlier
- Renders user-supplied input within a Thymeleaf template context (e.g., populating a model attribute from a request parameter and using it in a
th:text,th:href, or similar expression) - Relies solely on Thymeleaf's internal sandbox without additional input validation
You are likely safe if:
- You have already upgraded to Thymeleaf 3.1.4.RELEASE or newer
- Your application never passes unvalidated user input to the template engine context
- User input is strictly escaped and treated as data, never as template expressions
3. How to Detect It (Testing)
Manual Testing Steps
Step 1 — Identify Thymeleaf-rendered endpoints
Look for endpoints that reflect user input on the page (search fields, profile display pages, error messages, etc.). These are candidates where user data may flow into the template context.
Step 2 — Probe with a safe expression payload
Send a payload that, if evaluated by Thymeleaf, returns a predictable result:
GET /search?query=__$%7B7*7%7D__
# Decoded: __${7*7}__
If the response contains 49 instead of the literal string ${7*7}, the input is being evaluated as a Thymeleaf expression — the endpoint is vulnerable.
Step 3 — Confirm bypass with a blocked-class probe
Attempt to access a Java class that Thymeleaf's blocklist should deny:
GET /search?query=__$%7BT(java.lang.System).getenv()%7D__
If environment variables are returned rather than an error, the blocklist bypass (the CVE-2026-40477 condition) is confirmed.
Step 4 — Confirm RCE potential (in isolated test environments only)
GET /search?query=__$%7BT(java.lang.Runtime).getRuntime().exec(new+String[]{'/bin/sh','-c','id'})%7D__
Observe any out-of-band indicators (DNS callbacks, delayed responses) to confirm execution. Do not run this in production.
Automated Scanning
Tool: Nuclei (Project Discovery)
# nuclei template concept for CVE-2026-40477
id: CVE-2026-40477
info:
name: Thymeleaf SSTI Security Bypass
severity: critical
tags: ssti,thymeleaf,java,rce
requests:
- method: GET
path:
- "{{BaseURL}}/search?query=__$%7B7*7%7D__"
matchers:
- type: word
words:
- "49"
Run with:
nuclei -t cve-2026-40477.yaml -u https://your-target.com
Tool: Burp Suite (Active Scan)
Enable the active scanner and add the following to your custom scan insertion points targeting reflected parameters. The scanner will probe for expression evaluation markers. Use the Taborator or J2EEScan extension for enhanced Java SSTI detection.
Tool: OWASP ZAP
Enable the "Server Side Template Injection" passive and active scan rules. Add a custom scan rule targeting __${<expr>}__ payloads in reflected parameters.
Code Review Checklist
When auditing your Java codebase for this vulnerability, look for these anti-patterns:
- Check for direct model population from request parameters: Look for patterns like
model.addAttribute("query", request.getParameter("query"))where the value is later used in ath:textor template expression without escaping. - Search for
templateEngine.process()calls where the template name or fragment string is derived from user input. - Check for
th:utext(unescaped text) usage — this is especially dangerous as it bypasses Thymeleaf's HTML escaping. - Verify no user input reaches SpEL expressions within
th:if,th:each, or computed attribute bindings. - Confirm output encoding is applied before any user-supplied data enters the template model.
4. How to Fix It (Mitigation)
Step-by-Step Remediation
1. Upgrade Thymeleaf to 3.1.4.RELEASE or later
This is the only complete fix. The patch closes the whitespace parsing gap and hardens the expression sandbox blocklist to prevent the bypass chain exploited by CVE-2026-40477.
Update your Maven pom.xml:
<dependency>
<groupId>org.thymeleaf</groupId>
<artifactId>thymeleaf-spring6</artifactId>
<version>3.1.4.RELEASE</version>
</dependency>
Or in Gradle build.gradle:
implementation 'org.thymeleaf:thymeleaf-spring6:3.1.4.RELEASE'
If you are using Spring Boot, update your Spring Boot parent version (which manages Thymeleaf transitively) or explicitly override the version:
<properties>
<thymeleaf.version>3.1.4.RELEASE</thymeleaf.version>
</properties>
2. Run a dependency audit to identify transitive inclusions
# Maven
mvn dependency:tree | grep thymeleaf
# Gradle
./gradlew dependencies | grep thymeleaf
Ensure no transitive dependency is pulling in an older Thymeleaf version.
3. Never pass raw user input to the template context
Even after patching, adopt this as a permanent practice. All data entering the template model should be treated as plain data, not expressions.
4. Prefer th:text over th:utext
th:text HTML-encodes output; th:utext does not. Audit all templates and replace th:utext unless raw HTML is explicitly required and trusted.
5. Deploy and verify the patched application
Run your full regression test suite before deploying. After deployment, validate using the detection steps in Section 3.
Code Fix Example
Vulnerable pattern (before):
@GetMapping("/search")
public String search(@RequestParam String query, Model model) {
// ❌ User input passed directly into template context
model.addAttribute("query", query);
return "search-results";
}
<!-- search-results.html — renders user-controlled value in expression context -->
<p th:utext="${query}">Your search results</p>
Fixed pattern (after):
@GetMapping("/search")
public String search(@RequestParam String query, Model model) {
// ✅ Validate and sanitize before adding to model
String sanitizedQuery = HtmlUtils.htmlEscape(query);
model.addAttribute("query", sanitizedQuery);
return "search-results";
}
<!-- search-results.html — use th:text (HTML-encoded), not th:utext -->
<p th:text="${query}">Your search results</p>
Configuration Hardening
Beyond patching, restrict what objects are exposed in the Thymeleaf context:
@Bean
public SpringTemplateEngine templateEngine(ITemplateResolver templateResolver) {
SpringTemplateEngine engine = new SpringTemplateEngine();
engine.setTemplateResolver(templateResolver);
// Enable Spring Security integration for expression hardening
engine.addDialect(new SpringSecurityDialect());
// Do NOT expose sensitive beans to the template context by default
// Limit context variables to only what templates need
return engine;
}
Apply a Web Application Firewall (WAF) rule to block known SSTI payload patterns as a defense-in-depth measure while patches are being rolled out:
# ModSecurity / AWS WAF example — block Thymeleaf expression syntax
SecRule ARGS "@rx __\$\{.*\}__" "id:10001,phase:2,deny,status:400,msg:'Thymeleaf SSTI attempt detected'"
5. How to Test the Fix (Validation)
Regression Test Scenarios
- Scenario A: Confirm that the upgraded Thymeleaf version is active in the running application (check
/actuator/infoor/actuator/envin Spring Boot apps, or log the Thymeleaf version at startup). - Scenario B: Replay the detection payloads from Section 3 — confirm that
__${7*7}__is returned as a literal string and NOT evaluated to49. - Scenario C: Confirm all existing template rendering functionality works correctly — search results, error messages, profile pages, etc. should display expected output with no regressions.
Security Test Cases
Test Case 1: Verify SSTI Expression Evaluation Is Blocked
- Precondition: Thymeleaf upgraded to 3.1.4.RELEASE and deployed
- Steps: Send
GET /search?query=__$%7B7*7%7D__ - Expected Result: Response contains the literal string
__${7*7}__or an escaped equivalent — NOT49
Test Case 2: Verify Sandbox Bypass Is Closed
- Precondition: Patched application running
- Steps: Send
GET /search?query=__$%7BT(java.lang.System).getenv()%7D__ - Expected Result: Expression is not evaluated; no environment variable data is returned; application may return an error or the literal string
Test Case 3: Verify Functional Regression
- Precondition: Patched application running
- Steps: Perform a normal search with a legitimate query string (e.g.,
hello world) - Expected Result: Results are rendered correctly with no errors
Automated Tests
Add a security regression test to your Java test suite:
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@AutoConfigureMockMvc
class SstiSecurityTest {
@Autowired
private MockMvc mockMvc;
@Test
void sstiPayloadShouldNotBeEvaluated() throws Exception {
String sstiPayload = "__${7*7}__";
String encodedPayload = URLEncoder.encode(sstiPayload, StandardCharsets.UTF_8);
MvcResult result = mockMvc.perform(get("/search?query=" + encodedPayload))
.andExpect(status().isOk())
.andReturn();
String responseBody = result.getResponse().getContentAsString();
// The payload must NOT be evaluated — "49" must not appear
assertFalse(responseBody.contains("49"),
"SSTI payload was evaluated — CVE-2026-40477 may not be patched!");
// The literal string may be present (escaped) or sanitized away entirely
assertTrue(responseBody.contains("${7*7}") || !responseBody.contains("7*7"),
"Unexpected response behavior");
}
}
6. Prevention & Hardening
Best Practices
Practice 1 – Treat all user input as untrusted data, never as code. Never allow request parameters, headers, cookies, or any external data to flow directly into a template expression context. This is the single root cause of SSTI vulnerabilities.
Practice 2 – Pin and monitor dependency versions. Use tools like Dependabot, Renovate, or mvn versions:display-dependency-updates to be alerted when library updates (including security patches) are released. Subscribe to the Thymeleaf GitHub security advisories.
Practice 3 – Run SAST and SCA scans in CI/CD. Integrate tools like Snyk, Semgrep, or OWASP Dependency-Check into your pipeline to automatically flag known-vulnerable library versions and dangerous code patterns before they reach production.
Practice 4 – Limit what the template engine can see. Minimize the objects and beans exposed in the template context. If a template doesn't need access to a service or data object, don't add it to the model.
Practice 5 – Conduct regular security-focused code reviews of controller code and Thymeleaf templates, specifically looking for th:utext, dynamic template fragment selection, and unvalidated model attribute population.
Monitoring & Detection
Even with the patch applied, set up runtime detection for SSTI probing attempts:
WAF / API Gateway logging: Alert on requests containing __${, #{, T(java. or similar expression syntax in user-facing parameters.
Application logs: Add input validation middleware that logs (but does not evaluate) suspicious inputs matching expression patterns.
SIEM rules: Create detection rules for sequences of 4xx/5xx errors from a single IP against template-rendered endpoints — a classic sign of automated SSTI fuzzing.
# Example Splunk alert query
index=app_logs uri="/search" (query="*${*" OR query="*T(java*" OR query="*__$*")
| stats count by src_ip, uri
| where count > 5
References
- CVE Entry: CVE-2026-40477 – vulnerability.circl.lu
- GitLab Advisory: CVE-2026-40477 – Thymeleaf Expression Object Scope Restriction Bypass
- Technical Write-up: Thymeleaf SSTI Security Bypass (CVE-2026-40477) – TheHackerWire
- OpenCVE Details: CVE-2026-40477 – OpenCVE
- Feedly Threat Intel: CVE-2026-40477 – Feedly
- Related CVE (sibling issue): CVE-2026-40478 – Endor Labs write-up
- Patch: Thymeleaf 3.1.4.RELEASE on Maven Central
- OWASP SSTI Guide: Testing for Server-Side Template Injection