Tplmap: The Automated SSTI Hunter That Powers Penetration Tests
Hook
A single curly brace in a user input field can give an attacker complete server control—and most developers have no idea their template engine is the attack vector.
Context
Server-Side Template Injection emerged as a critical vulnerability class around 2015 when security researcher James Kettlebury demonstrated that template engines—designed to safely render dynamic content—could be weaponized for remote code execution. Unlike SQL injection or XSS, SSTI exploits lived in a blind spot: developers trusted template engines implicitly, often passing user input directly into templates without considering the attack surface. The problem compounds across ecosystems—a Python shop using Jinja2, a Node.js team on Handlebars, and a PHP project with Twig all face different SSTI vectors with unique syntax and escape techniques.
Before Tplmap, penetration testers faced a tedious manual process: inject test payloads for each suspected template engine, analyze responses for mathematical evaluation (does {{7*7}} return 49?), identify the specific engine and version, research known sandbox escapes, then craft exploitation chains by hand. This could take hours per parameter across applications using multiple template engines. Emilio Pinna created Tplmap in 2016 to automate this entire workflow—from detection through exploitation—making SSTI testing accessible to security researchers who weren't template engine specialists. The tool quickly became standard equipment in penetration testing arsenals, accumulating over 4,000 GitHub stars and appearing in virtually every bug bounty hunter's toolkit.
Technical Insight
Tplmap's architecture centers on a plugin system where each template engine gets dedicated detection and exploitation modules. The detection phase works by injecting polyglot payloads—test strings designed to trigger distinguishable responses across different engines. For example, the payload ${7*7} evaluates in Mako and FreeMarker but not in Jinja2, while {{7*7}} works in Jinja2, Twig, and Smarty. The tool fuzzes parameters with these polyglots, analyzes HTTP responses for evaluation signatures, then fingerprints the specific engine.
Once identified, Tplmap escalates through engine-specific exploitation chains. Here's a simplified example of how it might exploit Jinja2, demonstrating the sandbox escape technique:
# Jinja2 blocks direct access to dangerous modules, but allows
# traversing the object hierarchy to reach the os module
payload = "{{''.__class__.__mro__[1].__subclasses__()[396]('id',shell=True,stdout=-1).communicate()}}"
# Breaking this down:
# ''.__class__ gets the str class
# .__mro__[1] gets the object base class (everything inherits from object)
# .__subclasses__() lists all classes in the Python runtime
# [396] targets subprocess.Popen (index varies by Python version)
# ('id',shell=True,stdout=-1).communicate() executes the command
This payload demonstrates the core SSTI exploitation pattern: using template syntax to traverse Python's object model until reaching a class that enables OS command execution. Tplmap automates finding the correct subclass index, handling version differences, and chaining exploitation primitives. The tool maintains a database of these patterns across 15+ engines including Mako, Jade, Freemarker, Velocity, and Smarty.
The exploitation framework provides multiple post-detection capabilities through a pseudo-shell interface. After identifying a vulnerable parameter, you can invoke:
# Upload a reverse shell script
tplmap -u 'http://target.com/page?name=*' --upload /tmp/shell.sh /var/www/shell.sh
# Execute arbitrary commands
tplmap -u 'http://target.com/page?name=*' --os-cmd 'cat /etc/passwd'
# Establish an interactive shell
tplmap -u 'http://target.com/page?name=*' --os-shell
The asterisk * marks injection points, supporting POST data, headers, and cookies. Tplmap handles HTTP session management, encoding, and response parsing automatically.
For blind injection scenarios where application output isn't reflected in responses, the tool implements time-based and DNS-based detection. It might inject {{sleep(5)}} variants and measure response times, or trigger DNS lookups to an attacker-controlled domain to confirm code execution without visible output. The plugin architecture makes adding new engines relatively straightforward—each plugin defines detection regex patterns, exploitation primitives (read file, write file, execute command), and sandbox escape payloads as Python dictionaries.
The Burp Suite integration exposes Tplmap's detection engine through a right-click context menu, allowing testers to send interesting parameters directly from Burp's proxy or repeater tabs. This workflow integration proved crucial for professional penetration testers who live inside Burp during engagements, avoiding constant context switching to command-line tools.
Gotcha
The elephant in the room: Tplmap is officially unmaintained. The README explicitly states this, and the last significant commit was in 2019. This matters because template engines evolve—Twig 1.19+ patched the sandbox escapes Tplmap relies on, Smarty's secured mode blocks exploitation primitives, and newer engines like Nunjucks and Liquid have hardened against SSTI entirely. If you're testing modern applications with updated dependencies, Tplmap's success rate drops considerably. You'll identify the template engine but hit dead ends during exploitation because the escape vectors are patched. The tool won't tell you "this version is patched"—it just silently fails to escalate.
Detection reliability suffers against applications with aggressive rate limiting, WAFs (Web Application Firewalls), or complex multi-step workflows. Tplmap sends dozens of test payloads during fingerprinting, which triggers rate limits or ban rules. The tool has no built-in delay mechanisms or WAF evasion capabilities—if ModSecurity flags your traffic, you'll get false negatives. Applications requiring multi-step authentication or CSRF tokens often break Tplmap's automated approach since it doesn't maintain complex session state. You'll need to manually handle authentication cookies and dynamically update CSRF tokens between requests, which defeats the automation advantage. The blind injection detection is particularly brittle—network jitter can cause false positives in time-based detection, and DNS-based techniques require you to control a domain and set up a listener, adding operational overhead.
Verdict
Use if: You're performing penetration tests or bug bounties against legacy applications (pre-2019), need rapid SSTI detection across multiple template engines without manual payload crafting, or want to build custom exploitation workflows on top of an existing framework. It remains the fastest way to identify vulnerable template engines and establish whether SSTI exists before investing time in manual exploitation. The Burp integration alone justifies keeping it in your toolkit. Skip if: You're targeting modern, actively maintained applications with current template engine versions where exploitation will likely fail, need vendor support or active maintenance for production security tooling, or face heavily protected targets with WAFs and rate limiting where automated fuzzing approaches fail. In those cases, fork SSTImap for newer payloads or leverage Nuclei templates for detection-only workflows, then exploit manually using current research from PortSwigger and HackTricks.