Security News

Cybersecurity news aggregator

HIGH Vulnerabilities Snyk

Don't Panic: The Thymeleaf Template Injection That Only Hurts If You Let It (CVE-2026-40478)

CVE-2026-40478 is a critical (CVSS 9.0) server-side template injection vulnerability in the Thymeleaf Java templating engine, where the security sandbox can be bypassed using a tab character, potentially leading to remote code execution. The vulnerability affects Thymeleaf versions prior to 3.1.4, and the fix is to upgrade to version 3.1.4. Exploitation is conditional on a specific application-level misuse of the framework where user input is directly passed to the template expression engine, which is not the intended design pattern.
Read Full Article →

Snyk Blog In this article What the sandbox protects against Abusing the templating engine How the tab character breaks the Thymeleaf sandbox What you need to do The CVSS score 9.1 is real, but conditional Don't Panic: The Thymeleaf Template Injection That Only Hurts If You Let It (CVE-2026-40478) Written by Brian Vermeer April 29, 2026 0 mins read The Thymeleaf vulnerability with a CVSS score of 9.1 grabs your attention, as it should. But before you call the cavalry and claim this as the new Log4shell, read this first. CVE-2026-40478 is a server-side template injection vulnerability in Thymeleaf. Thymeleaf is a templating engine in Java that is used for server-side webpage rendering. The sandbox that normally prevents arbitrary code execution got bypassed using a tab character. And yes, this can lead to a remote code execution if exploited. But here is the part that matters most: this vulnerability only applies if your code is already doing something it shouldn't. What the sandbox protects against Thymeleaf has a security sandbox. It limits what SpEL (Spring Expression Language) expressions can do when they evaluate dynamic content. The sandbox exists for one specific situation: when user-controlled input somehow reaches Thymeleaf's expression engine. If that never happens in your code, the sandbox is never involved, and this CVE never touches you. The correct way to use Thymeleaf is simple: user input goes into the data model. The template stays static most of the time in an HTML file Java code: 1 @GetMapping( "/greet/safe" ) 2 public String greetSafe ( @RequestParam String name, Model model ) { 3 model.addAttribute( "name" , name); 4 return "greet" ; 5 } Thymeleaf template: 1 <!DOCTYPE html> 2 < html xmlns:th = "http://www.thymeleaf.org" > 3 < head > 4 < body > 5 < p th:text = "${name}" > Default name </ p > 6 </ body > 7 </ html > Thymeleaf renders the value of `name`. It never parses it as an expression. An attacker can send whatever payload they want as `name`, and nothing interesting happens. This is the design. This is how Thymeleaf is supposed to work. Abusing the templating engine The vulnerability becomes exploitable when a developer allows user input to reach the expression engine directly. If this occurs, it indicates the developer is misusing the framework. Although possible, it is quite challenging to achieve this, especially when using Thymeleaf within Spring Boot. 1 @ResponseBody 2 @GetMapping( "/greet/unsafe" ) 3 public String greetUnsafe ( @RequestParam String name, Model model ) { 4 Context context = new Context(); 5 SpringTemplateEngine templateEngine = new SpringTemplateEngine(); 6 templateEngine.setTemplateResolver( new StringTemplateResolver()); 7 context.setVariable(name, name); 8 String template = "<p th:text=\"'Hello ' + ${" + name + "}\">...</p>" ; 9 return templateEngine.process(template, context); 10 } As shown in the code above, I have to create a new instance of the templateEngine manually and set a resolver. Something that is normally provided by Spring. Next to that, I need to set the variable in the context to make my normal, legitimate use case work. However, in the case above, the user's input is passed to the expression engine. That's the precondition for this CVE to matter. A similar misuse pattern is dynamic view resolution. Instead of building template strings directly, a developer might resolve views based on user input: 1 @GetMapping( "/page/unsafe" ) 2 @ResponseBody 3 public String showPageUnsafe ( @RequestParam String page ) { 4 Context context = new Context(); 5 SpringTemplateEngine templateEngine = new SpringTemplateEngine(); 6 StringTemplateResolver resolver = new StringTemplateResolver(); 7 resolver.setTemplateMode(TemplateMode.TEXT); 8 templateEngine.setTemplateResolver(resolver); 9 context.setVariable(page, page); 10 String template = "[[${" + page + "}]]" ; 11 return templateEngine.process(template, context); 12 } The use case is legitimate, with one endpoint serving multiple pages. But user input is now influencing what Thymeleaf parses. Same misuse pattern, with the same possible exploit. Notice again how much manual wiring is required just to get here. The safe version is still just three lines. 1 @GetMapping( "/page/safe" ) 2 public String showPageSafe ( @RequestParam String page ) { 3 return page; 4 } How the tab character breaks the Thymeleaf sandbox After a misuse pattern is in place, the actual exploit is straightforward. The sandbox checks for new (the keyword followed by a space) to block object instantiation. The bypass sends `new[TAB]` instead. The pre-check doesn't find new, so it passes. SpEL, however, treats the tab as valid whitespace and parses it correctly. The payload looks like this: new[TAB]org.springframework.core.io.FileSystemResource('shell.jsp').getOutputStream() After the keyword check, a type blocklist runs, blocking `java.*` classes. However, Spring classes weren't included in this list. As a result, the incomplete blocklist, combined with a weak filter for new, allowed classes like FileSystemResource to load. This led to a file being written to disk. In theory, this allows an attacker to drop a JSP file that calls the ProcessBuilder and executes an RCE. Two defenses failed independently: a whitespace issue in the keyword check and a narrow blocklist. The bypass succeeded because the checks were incomplete on what was considered a separator. EndorLabs provided a more detailed analysis of the exploit if you're interested. Check out the GitHub repository for the working examples of the safe and unsafe code What you need to do Patch first. Update to Thymeleaf 3.1.4 regardless of whether you think your code is affected. Don't wait for the audit results. If you are using Thymeleaf via Spring Boot, simply update your Spring Boot starter parent to the latest version (currently 3.5.14 or 4.0.6). If it turns out that you are a few versions behind, you might want to take a look at the OpenRewrite recipes to migrate to the latest version of Spring Boot 3.5 or Spring Boot 4.0 After updating, examine your code to see whether template strings or page resolution are generated dynamically from user input by passing it directly to the template engine. If this occurs, it is a misuse pattern. Fix the code itself, not just the library version. The CVSS score 9.1 is real, but conditional A critical CVSS score assumes the precondition is met. If your code feeds user input into Thymeleaf's expression engine, a score of 9.1 is accurate, and the impact is severe. If it doesn't, you're not affected by the CVE itself. However, you should still patch! The audit question to ask your team is not just "what version of Thymeleaf are we running?" It's "do we dynamically construct view names or template expressions from request data?" Patch Thymeleaf to version 3.1.4 or beyond, then find out the answer to that second question. Regardless of the answer, keep scanning your dependencies in development and monitor them in production with Snyk Open Source. The best part is that you can start for free. Start securing your Python apps Find and fix Python vulnerabilities with Snyk for free. No credit card required. Start free with GitHub Start free with Google Or Sign up with Azure AD Docker ID Bitbucket By using Snyk, you agree to abide by our policies, including our Terms of Service and Privacy Policy . Posted in : Application Security Vulnerability Insights

Share this article