← 전체 Write-Up

Web / Dreamhack

Session / session-basic

Dreamhack 문제의 풀이와 실습 과정을 원문 그대로 담은 개인 기록입니다.

읽기 안내

동일 문제의 Session과 session-basic 기록을 한 페이지에 모았습니다. 두 원문의 코드와 서로 다른 실습 화면, 회고를 각각 보존했습니다.

원문 1 · Session

Session Notion 원문

문제 목표 및 기능 요약


해당 문제의 목표는 관리자 권한을 획득해 FLAG를 획득하는 것이다.

페이지 설명
/ 이용자의 username을 출력하고 관리자 계정인지 확인한다.
admin 관리자가 이용자의 세션 정보를 확인한다.

웹 서비스 분석

엔드포인트: /


다음 코드는 인덱스 페이지를 구성하는 코드다. 해당 페이지에서는 세션을 통해 이용자를 식별한다. 먼저 쿠키의 sessionid의 값을 통해 session_storage에서 해당 Session ID의 username을 조회한다. 만약 username이 "admin"일 경우 FLAG를 출력한다.

index 페이지 코드

@app.route('/') # / 페이지 라우팅
def index():
    session_id = request.cookies.get('sessionid', None) # 쿠키에서 sessionid 조회
    try:
        username = session_storage[session_id] # session_storage에서 해당 sessionid를 통해 username 조회
    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"}')

엔드포인트: /admin


admin 페이지 코드는 관리자 페이지를 구성하는 코드다. developer's note 아래 주석처리된 소스코드는 인덱스 페이지와 비슷한 구조로 세션을 통해 현재 접속한 이용자가 admin인지 확인하려고 했음을 볼 수 있다.

admin 세션 생성은 서비스 실행 시 os.urandom(32).hex()를 통한 무작위 값 생성을 통해 username이 admin인 세션 정보를 session_storage에 생성한다. 해당 session_storage 정보를 조회할 수 있다면 무작위 값을 추론하지 않고도 곧바로 Sesssion ID를 획득할 수 있음을 알 수 있다.

admin 페이지 코드

@app.route('/admin')
def admin():
    # developer's note: review below commented code and uncomment it (TODO)

    #session_id = request.cookies.get('sessionid', None)
    #username = session_storage[session_id] session_storage에 저장된 username을 불러옴
    #if username != 'admin': # username이 admin인지 확인
    #    return render_template('index.html')

    return session_storage

admin 세션 생성

if __name__ == '__main__':
    import os
    # create admin sessionid and save it to our storage
    # and also you cannot reveal admin's sesseionid by brute forcing!!! haha
    session_storage[os.urandom(32).hex()] = 'admin' # username이 admin인 Session ID를 무작위로 생성
    print(session_storage)
    app.run(host='0.0.0.0', port=8000)

취약점 분석


admin 페이지 코드를 다시 살펴보면 전체 세션 정보가 포함된 session_storage는 username이 admin인 관리자만 조회할 수 있도록 되어있다. 하지만 해당 부분은 개발자에 의해 주석처리 되었으므로 인증을 거치지 않고 session_storage를 조회할 수 있음을 알 수 있다.

@app.route('/admin')
def admin():
    # developer's note: review below commented code and uncomment it (TODO)

    #session_id = request.cookies.get('sessionid', None)
    #username = session_storage[session_id]
    #if username != 'admin':
    #    return render_template('index.html')

    # 인증을 수행하는 위의 코드가 주석처리되어 인증을 하지 않-

    return session_storage

익스플로잇


먼저 이용자 별 세션 정보를 조회한다. /admin 페이지에 접속하면 세션 정보 조회와 같이 현재 접속된 모든 이용자의 Session ID와 username을 조회할 수 있다.

세션 정보 조회

Session / session-basic — 실습 화면 1

이후 쿠키의 sessionid의 값을 admin의 Session ID로 생성해야 한다. 웹 브라우저를 통해 쿠키 변조와 같이 웹 브라우저의 개발자 도구를 사용하면 쿠키의 정보를 수정할 수 있다. admin의 Session ID가 적용된 상태에서 서버에 요청하면 FLAG 획득할 수 있다.

웹 브라우저를 통한 쿠키 변조

Session / session-basic — 실습 화면 2

후기


이번 문제에서는 제거되지 않은 디버그 코드를 통한 세션 하이재킹으로 자신 이외의 이용자에 대한 Session ID를 알아낼 수 있었다. 실습에서는 해당 취약점들을 연계하여 다른 계정의 권한을 획득할 수 있었다. 기본적으로 배포 전 디버그 코드가 남아있는지 검사한다면 이번 실습에서 나온 공격을 방지할 수 있다. 세션 하이재킹 공격에 대해서는 세션 타임아웃을 적용해 해결할 수 있다. 일정 주기마다 Sesison ID를 재발급해 정상 로그인한 이용자는 장기간 세션을 유지할 수 있는 반면, 탈취한 Session ID로 접속한 이용자는 일정 시간 이후 더 이상 해당 인증이 유효하지 않게 되므로 안전한 서비스를 구현할 수 있다.


원문 2 · session-basic

session-basic Notion 원문

웹 서비스 분석

엔드포인트 : /


index 페이지 코드

@app.route('/') # / 페이지 라우팅
def index():
    session_id = request.cookies.get('sessionid', None) # 쿠키에서 sessionid 조회
    try:
        username = session_storage[session_id] # session_storage에서 해당 sessionid를 통해 username 조회
    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"}')

엔드포인트 : \admin


admin 페이지 코드는 관리자 페이지를 구성한다.
admin 세션 생성은 서비스 실행 시 os.urandom(32).hex()를 통한 무작위 값 생성을 통해 username이 admin인 세션 정보를 session_storage에 생성한다. 해당 session_storage 정보를 조회할 수 있다면 무작위 값을 추론하지 않고도 곧바로 Session ID를 획득할 수 있다.

admin 페이지 코드

@app.route('/admin')
def admin():
    # developer's note: review below commented code and uncomment it (TODO)

    #session_id = request.cookies.get('sessionid', None)
    #username = session_storage[session_id] session_storage에 저장된 username을 불러옴
    #if username != 'admin': # username이 admin인지 확인
    #    return render_template('index.html')

    return session_storage

admin 세션 생성

if __name__ == '__main__':
    import os
    # create admin sessionid and save it to our storage
    # and also you cannot reveal admin's sesseionid by brute forcing!!! haha
    session_storage[os.urandom(32).hex()] = 'admin' # username이 admin인 Session ID를 무작위로 생성
    print(session_storage)
    app.run(host='0.0.0.0', port=8000)

취약점 분석


admin 페이지 코드를 보면 전체 세션 정보를 담고있는 session_storage는 username이 admin인 관리자만 조회하도록 의도되어 있다.
그러나 해당 로직이 주석처리가 되어있으므로 인증을 거치지 않고 session_storage를 조회할 수 있다.

익스플로잇


URL에 /admin을 추가로 입력하여 admin 페이지로 접속한다. 위에서도 말햇다시피 인증하는 로직이 주석처리가 되어있어 바로 관리자 페이지로 들어갈 수 있게 된다.

Session / session-basic — 실습 화면 1

관리자에 대한 Session ID 값이 나와있다. 알아낸 정보를 활용하기 위해 개발자 도구를 켜서 cookie 값을 변조하듯이 값을 넣고 새로고침을 하면 플래그가 뜬다.

Session / session-basic — 실습 화면 2

Session / session-basic — 실습 화면 3

후기


세션 하이재킹을 통해 자신 이외의 사용자에 대한 Session ID 값을 알아냈다.
이번 문제를 통해 느낀점은 디버깅을 위해 주석처리 해뒀던 코드가 있는지 배포 전에 다시 한번 더 확인하는 습관을 들여야겠다고 느꼈다.

작성 기록과 문제 출처

원문과 출처

풀이와 설명: MadEyE / 이선용의 개인 Notion 기록.
문제에서 제공한 코드·서비스 화면과 외부 참고자료의 권리는 해당 작성자와 플랫폼에 있습니다.

원문 본문·코드·실습 증적을 보존했으며, 원문에 포함된 외부 글은 참고 링크로 안내합니다.

이미지 원본 출처