Mô tả thử thách

Phân tích

Đầu tiên thử sử dụng Burp Scanner xem có lỗ hổng gì không

Vậy mình sẽ chuyển qua tìm kiếm trong source trước
Nhận thấy trong auth.py có đoạn xác thực user như sau
def confirm_register():
username = request.form['username']
email = request.form.get('email', '')
alphabet = string.ascii_letters + string.digits + '!@#$%^&*'
password = ''.join(secrets.choice(alphabet) for _ in range(12))
role = request.form.get('role', '2')
role_map = {
'1': 'editor',
'2': 'user',
}
if role == '0':
return jsonify({'error': 'Admin registration is not allowed.'}), 403
if role not in role_map:
return jsonify({'error': 'Invalid role id.'}), 400
uid = str(uuid4())
try:
with sqlite3.connect(DB_FILE) as db:
db.execute('INSERT INTO users (uuid, username, email, password, role) VALUES (?, ?, ?, ?, ?)',
(uid, username, email, password, role))
db.commit()
except sqlite3.IntegrityError:
return jsonify({'error': 'Username already exists.'}), 400
session['uuid'] = uid
session['first_login'] = True
session['first_login_password'] = password
return jsonify({
'success': True,
'redirect': '/home'
}), 200
Mình thấy nó sẽ tự động cấp cho người dùng mới tạo acc là role user với id là 2, trong code còn role editor với id là 1 và admin với id là 0
Ngoài ra nó còn chấp nhận trường role từ request nhưng lại chặn role với id là 0, tức admin, vậy mình sẽ thử tạo acc với role là 1 tức editor xem sao

Sau đó mình sẽ lấy session web gen cho để đăng nhập
Gửi request /home và Repeater và thay cookies

Vậy là đã có passwd để đăng nhập, thử đăng nhập vào trang web

Tiếp tục tìm kiếm trong source, tại file routes.py có một đoạn lấy dữ liệu người dùng theo uuid như sau
@app.route('/users/<string:target_uuid>')
def get_user_details(target_uuid):
current_uuid = session.get('uuid')
if not current_uuid:
return jsonify({'error': 'Unauthorized'}), 401
current_user = get_user_by_uuid(current_uuid)
if not current_user or current_user['role'] not in ('0', '1'):
return jsonify({'error': 'Invalid user role'}), 403
with sqlite3.connect(DB_FILE) as conn:
conn.row_factory = sqlite3.Row
c = conn.cursor()
c.execute("""
SELECT uuid, username, email, phone_number, role, password
FROM users
WHERE uuid = ?
""", (target_uuid,))
user = c.fetchone()
if not user:
return jsonify({'error': 'User not found'}), 404
return jsonify({
'uuid': user['uuid'],
'username': user['username'],
'email': user['email'],
'phone_number': user['phone_number'],
'role': user['role'],
'password': user['password']
})
Dữ liệu lấy ra sẽ được trả về dưới dạng file json và không mã hóa mật khẩu, sẽ là điểm thích hợp để đánh cắp passwd của admin nếu như biết uuid
Tiếp tục tìm kiếm trong source xem có chỗ nào leak được uuid của admin, nhận thấy có 1 endpoint accept collab mà không kiểm tra quyền, cho phép bất kỳ tài khoản nào gọi API để chấp nhận request và gây ra hành vi mà server thực hiện như admin đã accept
@app.route('/collab/accept/<string:request_uuid>', methods=['POST'])
def accept_collaboration(request_uuid):
if not session.get('uuid'):
return jsonify({'error': 'Unauthorized'}), 401
user = get_user_by_uuid(session['uuid'])
if not user:
return redirect('/login')
if user['role'] == '0':
return jsonify({'error': 'Admins cannot collaborate'}), 403
try:
with sqlite3.connect(DB_FILE) as conn:
conn.row_factory = sqlite3.Row
c = conn.cursor()
c.execute("SELECT * FROM collab_requests WHERE uuid = ?", (request_uuid,))
request = c.fetchone()
if not request:
return jsonify({'error': 'Request not found'}), 404
c.execute("""
INSERT INTO articles (uuid, title, content, author_uuid, collaborator_uuid)
VALUES (?, ?, ?, ?, ?)
""", (request['article_uuid'], request['title'], request['content'],
request['from_uuid'], request['to_uuid']))
c.execute("UPDATE collab_requests SET status = 'accepted' WHERE uuid = ?", (request_uuid,))
conn.commit()
return jsonify({'message': 'Collaboration accepted'})
except Exception as e:
return jsonify({'error': str(e)}), 500
Nhập bừa nội dung và chọn người collaborator là admin

Collab request đã được gửi cho admin

Copy uuid của request từ GET /collaborator

Gửi request này đến Repeater và chuyển method thành POST đồng thời chuyển URL thành /collab/accept/${uuid}

Yêu cầu đã được chấp nhận
Bây giờ chỉ cần quay lại bài viết và lấy ra uuid của admin

Sau đó sử dụng URL tìm kiếm user là /users/<uuid>

Bây giờ mình sẽ đăng nhập vào tài khoản admin
Bây giờ chúng ta sẽ truy cập vào /data như trong routes.py đã để lộ

Tải secrets.zip về

Nhận thấy cần mật khẩu để giải mã, mình tải tiếp dbconnect.exe về để tìm kiếm mật khẩu

Extrace file zip và lấy flag
Flag
Flag: Securinets{777_P13c3_1T_Up_T0G3Th3R}
'WriteUp > Web' 카테고리의 다른 글
| [Web] devtools-sources - Dreamhack (0) | 2025.10.11 |
|---|---|
| [Web] cookie - Dreamhack (0) | 2025.10.11 |
| [Web] Lunar File Invasion (SunshineCTF 2025) (0) | 2025.10.01 |
| [Web] Web Forge (SunshineCTF 2025) (0) | 2025.10.01 |
| [Web] Lunar Shop (SunshineCTF 2025) (0) | 2025.10.01 |
