본문 바로가기

드림핵 워게임

[드림핵/워게임] csrf-2 write-up

https://dreamhack.io/wargame/challenges/269/

 

csrf-2

여러 기능과 입력받은 URL을 확인하는 봇이 구현된 서비스입니다. CSRF 취약점을 이용해 플래그를 획득하세요. 문제 수정 내역 2023.07.18 css, html 제공 및 read_url() 코드 일부가 변경되었습니다. Referen

dreamhack.io

 

 

 

csrf-1과 다르게 login 페이지와 로그인 하라는 문구가 보인다.

 

 

 

guest:guest로 로그인을 해 보면 admin이 아니라는 문구가 뜬다.

 

코드를 살펴 보자!


코드

 

index

@app.route("/")
def index():
    session_id = request.cookies.get('sessionid', None)
    try:
        username = session_storage[session_id]
    except KeyError:
        return render_template('index.html', text='please login')

    return render_template('index.html', text=f'Hello {username}, {"flag is " + FLAG if username == "admin" else "you are not an admin"}')

1. 쿠키에서 세션 정보를 꺼내온다.

2. 로그인이 안 되어 있는 상태면 pleases login 문구를 띄운다.

3. username이 admin이면 flag를, 아니면 admin이 아니라는 문구를 띄운다.

 

 

login

@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(8).hex()
            session_storage[session_id] = username
            resp.set_cookie('sessionid', session_id)
            return resp 
        return '<script>alert("wrong password");history.go(-1);</script>'

1. GET 방식 -> html 파일 반환

2. POST 방식 -> 입력된 username, password를 받아 옴

3. 올바른 정보면 로그인 성공 -> 랜덤한 session_id를 만들어 session_storage[session_id]에 username 저장 후 session_id는 쿠키에 저장

 

flag

@app.route("/flag", methods=["GET", "POST"])
def flag():
    if request.method == "GET":
        return render_template("flag.html")
    elif request.method == "POST":
        param = request.form.get("param", "")
        session_id = os.urandom(16).hex()
        session_storage[session_id] = 'admin'
        if not check_csrf(param, {"name":"sessionid", "value": session_id}):
            return '<script>alert("wrong??");history.go(-1);</script>'

        return '<script>alert("good");history.go(-1);</script>'

1. GET 방식 -> html 파일 반환

2. POST 방식 -> 랜덤한 session_id를 만들어 session_storage[session_id]에 admin 저장

3. 쿠키에 session_id 저장

 

 

change_password

@app.route("/change_password")
def change_password():
    pw = request.args.get("pw", "")
    session_id = request.cookies.get('sessionid', None)
    try:
        username = session_storage[session_id]
    except KeyError:
        return render_template('index.html', text='please login')

    users[username] = pw
    return 'Done'

1. parameter로 pw 입력받기

2. 쿠키에 저장된 session_id에 해당되는 username을 가져와 pw로 비밀번호 변경

 


익스플로잇

1. admin에 해당되는 session_id를 쿠키에 저장하기 위해 flag 페이지에서 change_password 페이지 접근

2. admin 계정의 비밀번호를 자기가 원하는 pw로 바꿔 주기 (change_password의 파라미터로 비밀번호 넘기기)

3. admin 계정으로 로그인 후, 플래그 확인

4. vuln 페이지의 필터링에 걸리지 않게 하기

 

flag 페이지에서 다음과 같은 익스플로잇 코드를 넣어 주어 admin 계정의 비밀번호를 1234로 바꾸어 주겠다.

<img src="/change_password?pw=1234"/>

 

바뀐 비밀번호로 admin 계정에 로그인하면 플래그가 잘 뜨는 걸 볼 수 있다.