해킹공부/Dreamhack

[Dreamhack] session

밍구21 2022. 7. 26. 17:11

이번 문제를 풀 때 이전에 풀었던 session-basic 문제코드와 비교하며 풀었다. 그러므로 session-basic 풀이를 보고 오시는 걸 추천한다.

https://mimzz2.tistory.com/77

 

[Dreamhack] session-basic

[문제] session-basic [문제 파일] #!/usr/bin/python3 from flask import Flask, request, render_template, make_response, redirect, url_for app = Flask(__name__) try: FLAG = open('./flag.txt', 'r').rea..

mimzz2.tistory.com

 

 

 

[문제]

session

사진 클릭 시 문제 페이지로 이동

 

 

 

[문제파일]

#!/usr/bin/python3
from flask import Flask, request, render_template, make_response, redirect, url_for

app = Flask(__name__)

try:
    FLAG = open('./flag.txt', 'r').read()
except:
    FLAG = '[**FLAG**]'

users = {
    'guest': 'guest',
    'user': 'user1234',
    'admin': FLAG
}

session_storage = {
}

@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"}')

@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>'

if __name__ == '__main__':
    import os
    session_storage[os.urandom(1).hex()] = 'admin'
    print(session_storage)
    app.run(host='0.0.0.0', port=8000)

코드에서 얻을 수 있는 정보들

  • users에 저장된 세 계정. guest, user, admin
  • 위 계정으로 로그인 성공 시 index페이지에서 텍스트를 보여줌 → admin으로 성공 시 FLAG

 

이전 session-basic 문제에서 /admin 페이지 부분이 빠진 코드였다. 하지만 이전 문제에서 admin페이지가 힌트였기 때문에 이 부분이 빠진 코드에서는 다른 힌트가 존재할 거라고 생각해 코드를 더 살펴보았다. 그 결과 session id 생성 코드 부분이 다른 걸 볼 수 있었다. 

 

바뀐 코드 부분은 총 두 부분으로, 둘 다 세션 아이디 생성에 관한 코드였다.

1.

session_id = os.urandom(4).hex()

내가 로그인 할 때 세션값이 생성되는 원리 같다.

2.

session_storage[os.urandom(1).hex()] = 'admin'

마찬가지로 admin 계정의 세션값 생성 원리 같다고 추측했다.

 

 

이 부분을 활용하여 세션값 변조를 해야 될 것 같은데 이 부분이 어떻게 작용되나 잘 모르겠어서 문제 페이지에서 확인해보았다.

 

 

 


문제 페이지에서 로그인 시도를 먼저 해보도록 하겠다. 코드에 나온 guest 계정으로 로그인하였다.

 

 

 

로그인 결과, 세션 아이디가 session-basic문제와 다르게 확연히 짧아진 걸 보고 이 부분이 힌트인 걸 확신했다.

 

session_id = os.urandom(4).hex() < 이 코드대로 생성된 세션값이 d014a82a인 것을 확인했다. session-basic문제에서는 urandopn(32).hex()의 값이 64자리 16진수였다.

 

urandom(4).hex()의 결과로 8자리 16진수가 생성되는 걸 보아 urandopm(1).hex()의 결과는 2자리 16진수가 생성될 거라고 예측을 했고, 파이썬 코드로 실험해본 결과 두 자리 16진수가 세션값일 거라고 확신을 했다.

 

 

하지만 16*16=256개를 일일이 대입해보긴 힘들고,,,(사실 10개 정도 해보다가 오바라서 포기함. 운 좋으면 될 줄 알았죠^^) 검색 결과 버프스위트를 이용해 Brute-force를 할 수 있다고 해서 해보았다.

 

 

guest 계정으로 로그인한 메인페이지에서 proxy 탭에서 현재 sessionid 값을 선택해준 뒤 우클릭>send to intruder을 해준다.

(세션 아이디가 위와 다른 이유는 여러 번 풀어보면서 캡쳐 타이밍이 달랐기 때문...!)

 

 

그러며면 intruder 탭이 반짝거리는데 누르면 posotions탭에서 내가 방금 선택한 세션값 부분이 $$으로 선택되어 있다. 해당 값만 바꿔 공격해줄 것이니 범위가 잘 선택된 걸 볼 수 있다.

 

 

Payload 탭으로 넘어와 넣어줄 값을 설정해준다. payload type을 Brute forcer로 선택해준 뒤 character set에 16진수에 해당하는 1~f를 넣어주고 페이로드 길이를 2로 설정해준 뒤 우측 상단에 Start attack을 눌러주었다. 

 

 

 

세션id 값이 내가 설정해준 범위대로 하나씩 잘 가고 있다가 length 길이가 다른 페이로드 하나를 발견하였다. 그때 세션아이디가 94였고 이게 admin의 세션id임을 예측하였다.

 

 

 

94를 세션값으로 넣어주자 flag가 뜨는 걸 볼 수 있었다. 이 값을 드림핵에 입력하자 정답처리가 되었다!

(개발자도구에서 세션값을 변경해줘도 되지만 버프스위트에서 넣어줘도 된다.)