[GHCTF 2024 新生赛]理想国 flask session伪造

忙着毕业论文几天没做题了。
进入页面发现几个api接口,注册登录搜索登出4个。

利用postman访问注册接口注册。

可以看到返回了token,利用token访问login。

尝试search页面传入file参数试试能不能目录穿越。

得到secret-key,这里有个非预期解,访问/proc/1/environ直接得到flag。

预期解:访问/app/app.py得到源码。

点击查看代码
# coding=gbk
import json
from flask import Flask, request, jsonify, send_file, render_template_string
import jwt
import requests
from functools import wraps
from datetime import datetime
import os

app = Flask(__name__)
app.config['TEMPLATES_RELOAD'] = True
app.config['SECRET_KEY'] = os.environ.get('SECRET_KEY')
current_time = datetime.now().strftime('%Y-%m-%d %H:%M:%S')

response0 = {'code': 0, 'message': 'failed', 'result': None}
response1 = {'code': 1, 'message': 'success', 'result': current_time}
response2 = {'code': 2, 'message': 'Invalid request parameters', 'result': None}

def auth(func):
    @wraps(func)
    def decorated(*args, **kwargs):
        token = request.cookies.get('token')
        if not token:
            return 'Invalid token', 401
        try:
            payload = jwt.decode(token, app.config['SECRET_KEY'], algorithms=['HS256'])
            if payload['username'] == User.username and payload['password'] == User.password:
                return func(*args, **kwargs)
            else:
                return 'Invalid token', 401
        except:
            return 'Something error?', 500
    return decorated

def check(func):
    @wraps(func)
    def decorated(*args, **kwargs):
        token = request.cookies.get('token')
        if not token:
            return 'Invalid token', 401
        try:
            payload = jwt.decode(token, app.config['SECRET_KEY'], algorithms=['HS256'])
            if payload['username'] == "Plato" and payload['password'] == "ideal_state":
                return func(*args, **kwargs)
            else:
                return 'You are not a sage. You cannot enter the ideal state.', 401
        except:
            return 'Something error?', 500
    return decorated

@app.route('/', methods=['GET'])
def index():
    return send_file('api-docs.json', mimetype='application/json;charset=utf-8')

@app.route('/enterIdealState', methods=['GET'])
@check
def getflag():
    flag = os.popen("/readflag").read()
    return flag

@app.route('/api-base/v0/register', methods=['GET', 'POST'])
def register():
    if request.method == 'POST':
        username = request.json['username']
        if username == "Plato":
            return 'Your wisdom is not sufficient to be called a sage.', 401
        password = request.json['password']
        User.setUser(username, password)
        token = jwt.encode({'username': username, 'password': password}, app.config['SECRET_KEY'], algorithm='HS256')
        User.setToken(token)
        return jsonify(response1)
    return jsonify(response2), 400

@app.route('/api-base/v0/login', methods=['GET', 'POST'])
def login():
    if request.method == 'POST':
        username = request.json['username']
        password = request.json['password']
        try:
            token = User.token
            payload = jwt.decode(token, app.config['SECRET_KEY'], algorithms=['HS256'])
            if payload['username'] == username and payload['password'] == password:
                response = jsonify(response1)
                response.set_cookie('token', token)
                return response
            else:
                return jsonify(response0), 401
        except jwt.ExpiredSignatureError:
            return 'Invalid token', 401
        except jwt.InvalidTokenError:
            return 'Invalid token', 401
    return jsonify(response2), 400

@app.route('/api-base/v0/logout')
def logout():
    response = jsonify({'message': 'Logout successful!'})
    response.delete_cookie('token')
    return response

@app.route('/api-base/v0/search', methods=['POST', 'GET'])
@auth
def api():
    if request.args.get('file'):
        try:
            with open(request.args.get('file'), 'r') as file:
                data = file.read()
            return render_template_string(data)

        except FileNotFoundError:
            return 'File not found', 404
        except jwt.ExpiredSignatureError:
            return 'Invalid token', 401
        except jwt.InvalidTokenError:
            return 'Invalid token', 401
        except Exception:
            return 'something error?', 500
    else:
        return jsonify(response2)

class MemUser:
    def setUser(self, username, password):
        self.username = username
        self.password = password

    def setToken(self, token):
        self.token = token

    def __init__(self):
        self.username = "admin"
        self.password = "password"
        self.token = jwt.encode({'username': self.username, 'password': self.password}, app.config['SECRET_KEY'], algorithm='HS256')

if __name__ == '__main__':
    User = MemUser()
    app.run(host='0.0.0.0', port=8080)
所以实质就是伪造session,用户名为Plato,密码为ideal_state,secret-key已经知道了,直接在jwt.io进行下一步,利用伪造的session访问enterIdealState页面得到flag。
posted @ 2024-06-07 14:03  jockerliu  阅读(181)  评论(0编辑  收藏  举报