<<

csrf-1

wargame, web

문제

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

문제 분석

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

다만 XSS 문제와 다른 점은 서버 소스코드의 /admin/notice_flag 에서 플래그를 얻을 수 있다는 점과 이 요청을 핸들링하는 함수에서 요청자의 remote address 가 127.0.0.1 (localhost 자기 자신) 이고 요청의 쿼리스트링 useridadmin 가 아니라면 Access Denied 를 반환한다. 접속 주소를 확인하는 코드가 있기에 외부에서는 이 경로로 접근하여도 플래그를 얻을 수 없다.

@app.route("/admin/notice_flag")
def admin_notice_flag():
    global memo_text
    if request.remote_addr != "127.0.0.1":
        return "Access Denied"
    if request.args.get("userid", "") != "admin":
        return "Access Denied 2"
    memo_text += f"[Notice] flag is {FLAG}\n"
    return "Ok"

음… 그러면 서버에서 자기 자신에 요청하는거니까 그냥 XSS하면 되는거 아닌가? 🤔 라고 생각하고 XSS를 해보았다.

/vuln 에 입력해보았다
필터링된다
script, on 등의 키워드가 * 로 바뀌어있다. 문제의 취지에 맞게(?) XSS 관련 키워드들은 필터링이 되어있는 모습을 확인할 수 있다.

@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

서버 코드를 확인해보니 frame, script, on 을 필터링하고 있다. 그러면 이 필터링을 우회하여 CSRF 공격을 시도해보자.

CSRF 와 XSS 의 차이점:
CSRF 는 사용자의 브라우저가 타겟 서버에 요청을 보내는 것을 기반으로 공격하고 XSS 는 사용자의 브라우저가 악성 스크립트를 실행하는 것을 기반으로 공격한다. CSRF 는 타겟 서버가 요청을 신뢰하기 떄문에 발생하는 문제이고, XSS 는 사용자가 XSS스크립트가 삽입된 페이지를 신뢰하기 때문에 발생하는 문제이다.

CSRF 공격은 브라우저가 어떠한 리소스를 요청하는 과정에서 발생하기 때문에 <img> 태그, <link> 태그 등을 이용하여 공격을 시도할 수 있다.

풀이

스타일시트를 불러오는 <link> 태그를 이용하여 CSRF 공격을 시도하였다.

페이로드 보냄

<link rel="stylesheet" href="/admin/notice_flag?userid=admin" />
<img src="/admin/notice_flag?userid=admin">

위와 같이 /flag 페이지에 위 페이로드를 보낼 경우 서버에서 작동하는 브라우저는 위 태그가 삽입된 페이지에 접근하게 되고, 이 브라우저는 <link> 태그에 의해 /admin/notice_flag?userid=admin 에 요청을 보내게 된다. 올바르지 않은 요청이지만 서버는 이를 검증하지 않고 권한이 있는 사용자가 보낸 요청으로 받아들이게 되어 플래그를 메모 변수에 추가하고 공격자는 /memo 페이지에서 추가된 값인 플래그를 얻을 수 있다.

FLAG