<<

csrf-2

wargame, web

문제

CSRF 공격으로 플래그를 얻는 문제이다.

문제 분석

이 문제도 이전 csrf-1 과 유사하게 클라이언트의 취약점이기 때문에, 이를 트리거할 봇이 백엔드에 구현되어 있다.

서버 소스를 다운로드 받아 확인해보았다.

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

세션 아이디를 사용해 사용자를 식별하고, 사용자가 admin 일 경우에만 플래그를 출력하는 것을 확인할 수 있다.

@app.route("/vuln")
def vuln():
    param = request.args.get("param", "").lower()
    xss_filter = ["frame", "script", "on"]
    for _ in xss_filter:
        param = param.replace(_, "*")
    return param

또한 이전 문제와 마찬가지로 vuln 엔드포인트에서 xss 가 가능한 키워드들을 필터링하고 있다.

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

랜덤으로 만들어진 관리자의 세션 ID를 session_storage 딕셔너리에 저장하고 이전 문제의 check_csrf 코드처럼 서버에서 브라우저를 실행하고 그 브라우저에 쿠키로 관리자의 세션 ID를 설정한다.

이전 문제에서는 FLAG를 메모에 추가하는 엔드포인트를 CSRF 로 실행하였지만 이번 문제에서는

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

/change_password 엔드포인트가 있는 것을 볼 수 있다. 이 엔드포인트는 로그인되어있는 사용자의 비밀번호를 변경하는 엔드포인트이다.

풀이

이전 문제와 유사하게 <link> 태그를 이용하여 CSRF 공격을 수행하였다.

<link rel="stylesheet" href="http://localhost:8000/change_password?pw=1234" />

위 페이로드를 /flag 페이지 폼에 넣어 전송하면, 서버에서 실행 중인 관리자 권한을 가진 브라우저가 위 페이로드가 담긴 페이지를 로드하게 되고, 서버의 브라우저는 페이로드의 스타일시트를 로드하기 위해 /change_password 엔드포인트에 요청을 보내게 된다. 서버는 이 요청을 신뢰하여 admin 계정의 비밀번호를 1234 로 변경하게 된다.

이후 admin 계정으로 로그인하면 admin 로그인

flag 위 사진과 같이 플래그를 획득하게 된다.