본문 바로가기

드림핵

[드림핵 워게임] csrf-1

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

위의 문제를 다음과 같은 방법으로 풀어보았다.

 

 

 

1. 총 4개의 엔드포인트가 있는 것을 확인할 수 있다.

 

(1) vuln 페이지

@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_filter로 frame, script, on의 3가지 키워드를 필터링 하고 있어 xss 취약점을 보완하고 있는 것을 확인할 수 있다. 그러나 param 파라미터를 그대로 return하고 있으므로 필터링 되는 키워드를 사용하지 않는 코드에 대하여는 취약한 것을 알 수 있다.

 

(2) memo 페이지

@app.route("/memo")
def memo():
    global memo_text
    text = request.args.get("memo", None)
    if text:
        memo_text += text
    return render_template("memo.html", memo=memo_text)

/memo에서는 전역변수로 memo_text를 선언하고 있고, 사용자가 입력한 값을 memo_text에 담아 렌더링되는 페이지에 출력하는 것을 확인할 수 있다.

 

(3) notice flag 페이지

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

notice flag 페이지에서는 전역변수 memo_text에 flag를 저장하고 있다. 그러나 호스트가 로컬호스트(127.0.0.1)가 아니거나 userid값이 admin이 아닌 경우에는 접근이 허용되지 않는다. 따라서 flag를 얻기 위해서는 호스트를 로컬호스트로 하고, 또한 userid값을 admin으로 하여 접근해야 한다.

 

(4) 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", "")
        if not check_csrf(param):
            return '<script>alert("wrong??");history.go(-1);</script>'

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

/flag에서는 GET과 POST 두개의 메소드를 볼 수 있는데, 인덱스 페이지에서 flag 페이지로 접속할 때는 GET으로 요청되어 url을 입력할 수 있는 페이지로 이동하고, 해당 페이지에서 url을 입력하여 제출하면 POST로 요청되어 입력한 값을 param 파라미터를 통해 전달받아 check_csrf() 함수를 실행한다.

 

def check_csrf(param, cookie={"name": "name", "value": "value"}):
    url = f"http://127.0.0.1:8000/vuln?param={urllib.parse.quote(param)}"
    return read_url(url, cookie)

check_csrf() 함수에서는 param 파라미터로 전달받은 입력값으로 url을 완성하고, read_url() 함수를 실행하여 완성된 url을 방문한다. 이때 url은 로컬호스트(127.0.0.1)로 요청하고 있으므로 앞서 살펴본 notice flag에서 flag를 얻기 위해 만족해야하는 조건(로컬호스트, userid=admin) 중 하나를 충족시키고 있다. 또한 취약점이 있는 vuln 페이지로 이동하는 요청을 하고 있으므로 flag 페이지에서 param 파라미터에 익스플로잇 코드를 작성해 제출하면 공격에 성공할 수 있을 것이다.

 

 

 

2. csrf 취약점 발생 가능 여부를 확인한다.

 

csrf 공격이 가능한지 테스트하기 위해 드림핵 툴즈(https://tools.dreamhack.games)를 이용했다. 드림핵 툴즈에서 좌측 메뉴의 Request Bin 탭으로 들어가면 랜덤 링크가 생성되는 것을 볼 수 있다. 생성된 링크를 이용해 flag 페이지에 아래와 같은 csrf 테스트 코드를 입력하여 제출한다. 이때, vuln에서 필터링하고 있는 키워드 frame, script, on 을 이용한 코드는 공격이 차단되므로 img 등 그외의 태그를 이용하여 코드를 작성한다.

 

<img src="https://랜덤문자열.request.dreamhack.games">

 

테스트 코드를 제출하고 드림핵 툴즈 Request Bin 페이지를 새로고침하면 생성된 링크로 접속한 기록이 뜨는 것을 볼 수 있다. 즉 flag 페이지에 입력한 코드로 csrf 공격이 가능한 것을 확인한 것이다.

 

 

 

3. 익스플로잇 코드를 입력하여 제출한다.

 

이제 notice flag 페이지로 접속하여 flag를 성공적으로 memo_text 변수에 저장하여 memo 페이지에 flag가 출력되도록 익스플로잇 코드를 작성하면 된다.

<img src="/admin/notice_flag?userid=admin"/>

필터링되는 키워드를 피해 img 태그를 사용하였고, admin/notice_flag 에서 userid가 admin이 아닌 경우에 Access Denied 되는 if문을 통과하기 위해 userid에 admin을 주어 전달하였다. 이렇게 짠 익스플로잇 코드를 flag 페이지에 입력하여 제출한다.

 

 

 

4. memo 페이지에 접속하면 flag가 뜬 것을 확인할 수 있다.

'드림핵' 카테고리의 다른 글

[드림핵 워게임] simple-sqli  (1) 2022.09.24
[드림핵 워게임] csrf-2  (1) 2022.09.24
[드림핵 워게임] xss-2  (1) 2022.09.21
[드림핵 워게임] xss-1  (1) 2022.09.21
[드림핵 워게임] session-basic  (0) 2022.09.18