ssti模板注入原理
ssti模板注入是一種基于服務器的模板引擎的特性和漏洞產生的一種漏洞,通過將而已代碼注入模板中實現的服務器的攻擊
模板引擎
為什么要有模板引擎
在web開發中,為了使用戶界面與業務數據(內容)分離而產生的,它可以生成特定格式的文檔,用于網站的模板引擎就會生成一個標準的HTML文檔。在不同的技術領域下有很多模板引擎,比如php和c等等都有各自的模板引擎
模板定義
模板是一種包含占位符和靜態內容的文本文件,占位符用于表示在運行時將被動態替換的數據或執行的邏輯。例如,在一個 HTML 模板中,可能會有{{ name }}
這樣的占位符,用于表示用戶的姓名。
模板的渲染和替換
當應用程序需要生成最終的頁面或文檔時,模板引擎會讀取模板文件,并根據提供的數據將占位符替換為實際的值。同時,模板引擎還會執行模板中定義的各種邏輯,如循環、條件判斷等,最終生成完整的 HTML 或其他格式的文檔發送給客戶端。以{{name}}為例在模板引擎渲染的過程中,name這個字符串就可能被動態數據內容替換,或者在進行一定邏輯運算之后產生新的字符串,例如在HTML文檔中,如果提供了名為name
且值為 “張三” 的數據,那么渲染之后的效果就是{{張三}}
原理
模板引擎有很多種類型,例如最簡單的置換型,從名稱上我們也可以大致推斷他的用處,還有復雜一點的解釋型、編譯型等等模板引擎。不同的模板引擎可以應用于不同的場景。
使用場景
模板引擎可以讓(網站)程序實現界面與數據分離,業務代碼與邏輯代碼的分離,這就大大提升了開發效率,良好的設計也使得代碼重用變得更加容易。還能實現其他類的功能。
ssti模板注入產生的原因
程序可能在沒有經過完善的驗證和過濾的情況下,將用戶的輸入直接嵌入到模板中或者作為參數嵌入到模板引擎中,如果用戶輸入包含惡意代碼,可能造成意想不到的代碼執行效果。
用大白話舉個例子:網站程序可能給你提供了向網站輸入數據的途徑,但是對于你的輸入,網站對他的處理不是很到位,導致你的輸入可能經過模板引擎渲染之后執行了,產生了一些效果,或者是你的輸入直接破壞了模板,導致之后沒達到渲染效果或者是敏感信息泄露、getshell等等問題。
所以在了解完產生原因之后我們肯定也知道了,對于不同的模板,他的邏輯可能不一樣,表達的字符串也可能不一樣,各種方面都可能不一樣;所以對于不同的模板,注入的代碼可能也不一樣,注入的形式可能也不一樣……
ssti一般的注入過程
注入前
了解注入網站程序的模板信息、編程語言等等;在之后注入時構造payload時要根據這兩個方面進行。
了解一些模板引擎的特點和性質等等,比如python的jinja2,php的Smarty、Twig和Blade,java的Thymeleaf 和Velocity 等等,后面用的flask框架用的就是python的jinja2
注入
根據搜集到的信息構造payload,例如,在 Jinja2 中,可以使用{{ }}
來包裹表達式,使用{% %}
來包裹控制語句。{{ config['SECRET_KEY'] }}
可以用于獲取 Flask 應用程序的密鑰。? ? ? ? ??? ? ? ? ?{{ __import__('os').system('ls -la') }}
可以在服務器上執行ls -la
命令,列出當前目錄下的文件和目錄信息。
找到注入點,可能是登錄注冊的輸入欄,也可能時評論的輸入欄等等,如果有源碼,可以進行代碼審計找到注入點,或者通過其他辦法找到注入點。
Flask
定義
Flask是一個使用?Python?編寫的輕量級?Web 應用框架。其?WSGI?工具箱采用 Werkzeug ,模板引擎則使用 Jinja2 。Flask使用 BSD 授權。
由于塔容易上手,我們使用這個來嘗試一下ssti漏洞。
這是一個簡單的py代碼,調用了flask庫
from flask import Flask
app = Flask(__name__)
@app.route('/')
def index():return '你好'
if __name__ == '__main__':app.run()
代碼的用途可以去了解一下,我們運行這個代碼,在本地網址5000端口可以看到回顯
我們嘗試搭建一個用來測試ssti模板注入的網站
from flask import Flask, request, render_template_string
app = Flask(__name__)@app.route('/safe')
def safe():name = request.args.get('name', 'World')if any(char in name for char in ['{', '}', '%', '#']):name = '非法輸入'template = '<h1>安全頁面:你好,{{ name }}!</h1>'return render_template_string(template, name=name)@app.route('/unsafe')
def unsafe():name = request.args.get('name', 'World')template = f'<h1>不安全頁面:你好,{name}!</h1>'return render_template_string(template)@app.route('/danger')
def danger():expr = request.args.get('expr', '2+2')try:result = eval(expr)except Exception as e:result = f"錯誤: {str(e)}"template = f'<h1>表達式結果: {result}</h1>'return render_template_string(template)if __name__ == '__main__':app.run(host='0.0.0.0', port=8080, debug=True)