intigriti Easter XSS challenge

文章首发于先知

前言

国外的一个xss challenge(规定时间内做出可得一年的Burp Suite正版证书)

JY7U10.png

题目分析

主要的功能逻辑代码为script.js, 如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
var hash = document.location.hash.substr(1);
if(hash){
displayReason(hash);
}
document.getElementById("reasons").onchange = function(e){
if(e.target.value != "")
displayReason(e.target.value);
}
function reasonLoaded () {
var reason = document.getElementById("reason");
reason.innerHTML = unescape(this.responseText);
}
function displayReason(reason){
window.location.hash = reason;
var xhr = new XMLHttpRequest();
xhr.addEventListener("load", reasonLoaded);
xhr.open("GET",`./reasons/${reason}.txt`);
xhr.send();
}

代码逻辑为:

通过触发location.hash的变化来调用displayReason函数,通过ajax请求得到./reasons/${reason}.txt,然后把response的内容返回给id为reason的标签。

首先测试了下直接在URL的锚部分写上xss语句

JtR4Zn.png

这里可以看到这个404的页面,在response时不仅对特殊字符进行了编码处理,而且还会将%替换为_,所以直接通过xss语句触发是没戏了。

接下来尝试了一下.htaccess等403页面的触发,看下对应的response内容有没有经过处理

JtfOER.png

可以看到,在403的response中我们的xss语句是完整存在的,那么接下来只需要把这个xss带到我们之前的页面就可以触发xss了

进行尝试

这里需要注意两个地方

  • 第一个是默认ajax去请求是下reasons文件夹下,我们如果要请求.htaccess是需要向上跳一级目录的
  • 第二个是我们需要拿到的是访问htaccess得到的response,所以htaccess的参数需要进行二次编码

最终访问效果如下:

JtIBZQ.png

可以看到img标签成功被解析。

不过此时仍然没法弹窗,因为这个challenge是有csp进行限制的,csp规则如下:

1
default-src 'self';

限制了资源必须来自此站点

那么接下来就开始寻找一些可以当成js来执行的地方,我们可以通过之前的404页面返回的内容来当作我们的js语句,并且通过script标签的src属性进行引入,唯一需要注意的是要进行单引号的闭合

构造语句如下:

JtbPAg.png

在控制台执行一下

JtbQN4.png

成功触发,接下来把这个返回内容作为外部js引入就可以了

不过不能直接在response里面直接返回script标签,因为这里的赋值是

1
reason.innerHTML = unescape(this.responseText);

通过innterHTML产生的script标签并不可以执行

所以这里可以用iframe的srcdoc属性去解决这个问题,payload如下:

1
<iframe/srcdoc='<script src="x%27-alert(document.domain)-%27"></script>'>

将上面的payload二次编码后,放进location.hash

成功触发xss

JtvIGF.png