A security researcher demonstrated the exploitation of a Server-Side Template Injection (SSTI) vulnerability in Spring Boot 3.3.4 using the Thymeleaf templating engine. During the testing process, remote code execution (RCE) was achieved despite the framework’s built-in security mechanisms.
During a penetration test, it was discovered that the Referer
header was being used in a Thymeleaf template without proper escaping. Analysis revealed that the template engine applied a mechanism of double expression evaluation, enabling the injection of arbitrary Java code.
Initial exploitation attempts were hindered by HTML encoding, as injected single quotes were reflected in encoded form, initially impeding further progress. However, Thymeleaf’s parser corrected syntax errors, allowing arbitrary expressions to execute.
Local testing confirmed code execution feasibility. Injecting the expression ${7*7}
returned 49
, verifying the presence of SSTI. However, attempts to execute system commands using an SSTI payload:
${T(java.lang.Runtime).getRuntime().exec('calc')}
resulted in a TemplateProcessingException
. Further investigation revealed that Thymeleaf restricts access to static classes and methods within templates.
To bypass these restrictions, the researcher utilized reflection. Despite the application’s safeguards blocking many dangerous classes, it included the Apache Commons Lang3 library, which contains the MethodUtils
class, enabling the execution of private methods.
This class facilitated the bypass of protections, allowing the invocation of the Runtime.getRuntime()
method to execute commands:
"".class.forName("org.apache.commons.lang3.reflect.MethodUtils")
.invokeMethod(
"".class.forName("java.lang.Runtime")
.getMethod("getRuntime")
.invoke(null),
"exec",
"whoami"
)
The whoami
command executed successfully, confirming remote code execution capabilities.
Due to restrictions on outbound connections, the researcher leveraged file input/output to transmit data. The Referer
header allowed writing results to a file, which could then be retrieved through an HTTP response:
bash -c {echo,d2hvYW1p}|{base64,-d}|{bash,-i}>abc
The final exploit enabled remote command execution but required file output for data transmission due to environmental limitations.