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 | |
实验一:反射型 XSS(未经编码处理)
提示: 搜索功能存在反射型 XSS 漏洞。由于输入内容会未经编码直接回显到 HTML 上下文中,因此可构造脚本触发 alert() 完成实验。
搜索框输入 XSS 弹窗代码。
1 | |
实验二:存储型 XSS(未经编码处理)
提示: 评论功能存在存储型 XSS 漏洞。评论内容会被保存并在查看博客文章时回显到页面中,且未经过编码处理,因此可提交包含 alert() 的脚本,在文章页面加载评论时触发弹窗,完成实验。
找到任意一个评论功能,输入 XSS 弹窗代码。
1 | |
实验三:DOM 型 XSS(document.write)
提示: 搜索查询跟踪功能存在基于 DOM 的 XSS 漏洞。页面使用 document.write 将 location.search 中的数据直接写入页面,且未进行安全处理,因此可通过构造 URL 参数注入脚本并触发 alert(),完成实验。
与反射型和存储型不太一样,DOM 型需要查看页面执行脚本构建 payload,点击检查,定位到搜索元素,在下方会定义一个 javascript 脚本,该脚本定义了一个 trackSearch 函数,并在下方执行了该函数,通过 search 传入 query 参数,最后将 query 内容直接传入 document.write 执行,未进行任何过滤,只需要将 img 标签闭合,即可触发 XSS 弹窗。

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

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

1 | |
解释: 页面通过 innerHTML 将输入内容插入到 DOM 中。虽然 <script> 标签会被写入页面结构,但通常不会自动执行,因此需要构造可触发事件的 HTML 标签,并通过事件处理函数执行 alert() 完成实验。
实验五:DOM 型 XSS(href 属性)
提示: 反馈提交页面存在基于 DOM 的 XSS 漏洞。页面使用 jQuery 的 $ 选择器获取锚点元素,并将 location.search 中的数据写入其 href 属性。可通过构造 javascript: 协议控制 Back 链接,使其在点击时执行 alert(document.cookie),完成实验。
这道题考查点击触发 DOM 型 XSS,找到提交评论的页面,检查源码在返回按钮下方可发现一个 function 函数,由于它通过 $ 符号包裹,意味着它会自动运行函数。

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

注意: 此时无法通过闭合 href 标签直接执行 XSS 代码,主要由于当前自动运行的函数将输入的内容赋值给了 href。
实验六:DOM 型 XSS(hashchange)
提示: 首页存在基于 DOM 的 XSS 漏洞。页面使用 jQuery 的 $() 选择器处理 location.hash 中的数据,并在 hashchange 事件触发时根据该值定位文章标题。可通过构造恶意片段标识并诱导受害者访问,使页面在处理该值时执行 print(),完成实验。
找到页面源码执行的 script 脚本位置。

1 | |
该漏洞点绑定在 hashchange 事件上,因此通常需要在页面加载完成后再修改 URL 片段,才能触发后续的 jQuery 选择器处理逻
1 | |
但是要完成该实验室需要借助环境提供的 exploit server。
1 | |


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

1 | |
除了鼠标悬停触发还有一个其他 HTML 事件处理属性。
1 | |
实验八:存储型 XSS(双引号被 HTML 编码)
提示: 评论功能存在存储型 XSS 漏洞。评论作者姓名对应的链接地址会被写入锚点的 href 属性中,虽然双引号被进行了 HTML 编码,但仍可通过构造 javascript: 伪协议,在用户点击评论作者姓名时调用 alert(),完成实验。
找到评论功能,用户的网站被插入到 href 属性中,由于当前无法闭合双引号,可插入 javascript 执行 XSS。

1 | |
实验九:反射型 XSS(尖括号被 HTML 编码)
提示: 搜索查询跟踪功能存在反射型 XSS 漏洞。用户输入会回显到 JavaScript 字符串中,虽然尖括号被进行了 HTML 编码,但仍可通过构造输入突破字符串上下文并执行 alert(),完成实验。
审查页面源码,发现有一个 javascript 代码,他会接受搜索时传递的值,将其赋值到 searchTerms 变量,然后 将其拼接到 img 标签,这里无法输入尖括号尝试闭合,但是程序没有过滤输入的单引号,单引号会导致 var 语句错误,可以在单引号中插入 alert 语句完成实验。


1 | |

实验十:DOM 型 XSS(document.write + select 元素)
提示: 股票查询功能存在基于 DOM 的 XSS 漏洞。页面使用 document.write 将 location.search 中的可控数据写入 select 元素中,因此可通过构造输入跳出 select 上下文并执行 alert(),完成实验。
点击商品详情页面,页面可下拉查询商品库存数量,查看页面源码,发现一个 script 脚本内容,脚本接受 URL 的 storeId 参数,然后将其直接插入到页面源码,可通过该参数插入 XSS payload。

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


执行已知 payload 可触发 XSS 漏洞。
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 | |

实验十三:存储型 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 | |
注意: <script> 报错的 alert 无法执行,因为他是页面加载后通过 DOM 写入到页面的,需要使用 img 标签触发错误执行 alert。
实验十四:反射型 XSS(WAF 过滤绕过)
提示: 搜索功能存在反射型 XSS 漏洞,但常见标签和属性会被 WAF 过滤。可通过构造未被拦截的标签与事件处理方式绕过过滤,并调用 print() 完成实验。
当搜索内容包含 HTML 标签时,服务器会响应 400,复制官方速查表所有标签,然后通过 burp 爆破模块模糊测试。
1 | |


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

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


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

实验十五:反射型 XSS(仅允许自定义标签)
提示: 搜索功能存在反射型 XSS 漏洞。页面会屏蔽常见 HTML 标签,仅允许自定义标签存在,因此可通过注入自定义标签并结合可自动触发的事件处理方式,调用 alert(document.cookie) 完成实验。
与上个实验相似,标签枚举 -> 属性枚举,发现在自定义标签中没有对属性进行任何过滤。




构造一个自定义标签获取网站 Cookie。
1 | |
在页面测试可正常弹出 Cookie,实验需要通过 exploit 发送,可以通过 script 包裹 location 执行 payload。
1 | |

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

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

构建 payload:
1 | |
实验十七:反射型 XSS(canonical 链接标签)
提示: 用户输入会反射到页面的 canonical link 标签属性中,虽然尖括号被进行了转义,但仍可通过闭合原有属性并注入新的事件属性完成利用。可结合 accesskey 与事件处理函数构造 payload,在 Chrome 浏览器中通过指定快捷键触发 alert(),完成实验。
改变 URL 参数,页面源码 header <link rel="canonical" 也会发生改变,说明后端会读取 URL 并写入网站前端。

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

一旦客户端输入快捷指令就会触发 XSS,注意: 仅在谷歌浏览器生效。
1 | |
实验十八:反射型 XSS(JavaScript 字符串中的单引号与反斜杠转义)
提示: 搜索查询跟踪功能存在反射型 XSS 漏洞。用户输入会回显到 JavaScript 字符串中,且其中的单引号与反斜杠会被转义,但仍可通过构造输入突破字符串上下文并执行 alert(),完成实验。
检查网页源码,在搜索位置发现一个 script 标签,标签内的 searchTerms 变量值为搜索传递的参数。

后续通过 document.write 写入 img 标签,并且进行了参数的 URL 编码,所以这里没有办法闭合绕过 img 标签,但可以传入 </script> 闭合前面的 <script> ,然后执行 alert。
1 | |
实验十九:反射型 XSS(JavaScript 字符串中的单引号转义)
提示: 搜索查询跟踪功能存在反射型 XSS 漏洞。用户输入会回显到 JavaScript 字符串中,尖括号和双引号会被 HTML 编码,单引号会被反斜杠转义,但反斜杠本身未被处理,因此可通过构造输入突破字符串上下文并执行 alert(),完成实验。
和上一个实验代码一致,依然是通过 script 包裹了一串 javascript 代码,但是它会对左右尖括号做替换。

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

构建 payload:
1 | |

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

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

实验二十一:反射型 XSS(模板字面量)
提示: 搜索博客功能存在反射型 XSS 漏洞。用户输入会回显到 JavaScript 模板字面量中,尖括号、单引号和双引号会被 HTML 编码,反引号和反斜杠会被转义,但仍可通过模板字面量表达式突破当前上下文并调用 alert(),完成实验。
查看页面源码,发现搜索执行结果会通过 document.getElementById 赋值,在它之前会传入服务器做一次过滤,这次过滤会禁用转义符号、单引号、双引号等。

通过模版字面量的表达式注入 alert。
1 | |
实验二十二:存储型 XSS(窃取 Cookie)
提示: 博客评论功能存在存储型 XSS 漏洞。攻击者可在评论中注入恶意脚本,当受害者查看评论时,脚本会自动执行并将其会话 Cookie 发送到攻击者可控位置。获取受害者 Cookie 后,将其写入浏览器请求中即可冒充受害者,完成实验。
完成实验需要使用官方的 NDSlog,该功能集成在专业版 burp 中。

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

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

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


官方 payload:
1 | |
实验二十三:存储型 XSS(窃取密码)
提示: 博客评论功能存在存储型 XSS 漏洞。攻击者可在评论中注入恶意脚本,当受害者查看评论时,脚本会自动执行并窃取其输入的用户名和密码。获取凭据后,使用对应账号密码登录受害者账户即可完成实验。
创建一个 HTML 表单,用于接受受害者提交的用户名密码。
1 | |


1 | |
通过 administrator 用户登录网站。
实验二十四:存储型 XSS(绕过 CSRF 防御)
提示: 博客评论功能存在存储型 XSS 漏洞。攻击者可在评论中注入恶意脚本,当用户查看评论时,脚本会自动执行并窃取页面中的 CSRF Token,再利用该 Token 发起修改邮箱请求,从而完成对受害者账户邮箱地址的更改。
已知用户名密码: wiener:peter 。
检查登录数据包,发现 Cookie 设置了 HttpOnly,说明无法通过 XSS 漏洞获取用户 Cookie 了。

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

CSRF 值可以在页面源码获得。
1 | |
官方 payload:
1 | |
实验二十五:反射型 XSS(AngularJS 沙箱逃逸)
提示: 页面在 AngularJS 表达式上下文中处理用户输入,且 $eval 不可用,同时无法直接使用字符串。可通过构造无需引号的 AngularJS 沙箱逃逸表达式,覆盖关键原型方法后再借助 orderBy 过滤器执行 alert(),完成实验。
程序使用了 AngularJS 框架。前端定义了一个控制器,通过 $parse() 对表达式进行解析并获取对象中的对应值。

输入之前 AngularJS 默认的表达式,页面没有触发 XSS,可能存在沙箱过滤。
程序原本将 key 固定为 search,并通过 $parse(key) 对其进行解析,再从 $scope.query 对象中取出对应的值。我们需要将绕过 AngularJS 沙箱的 payload 写成 GET 请求参数中执行。
1 | |

实验二十六:反射型 XSS(AngularJS 沙箱逃逸 + CSP 绕过)
提示: 页面同时使用了 CSP 和 AngularJS。用户输入会进入 AngularJS 表达式上下文中,需构造能够绕过 CSP 并逃逸 AngularJS 沙箱的表达式,在页面解析时调用 alert(document.cookie),完成实验。
构建 payload:
1 | |
实验二十七:反射型 XSS(事件与 href 被阻止)
提示: 页面存在反射型 XSS 漏洞。虽然仅允许部分白名单标签,且事件处理属性与锚点 href 属性均被阻止,但仍可通过构造可点击的利用向量,在用户点击标记为 “Click” 的内容后调用 alert(),完成实验。
1 | |
通过 svg 标签 animate 属性绕过限制,
1 | |

实验二十八:反射型 XSS(JavaScript URL 字符过滤)
提示: 用户输入会反射到 javascript: URL 中,且部分字符被应用程序过滤。可通过构造绕过过滤的 JavaScript URL,在执行时调用 alert(),并使弹窗内容中包含 1337,完成实验。
点击任意 view post,发现返回主页的按钮通过 href 传递了一个 javascript 语句。


该语句除了返回主页还以 POST 向 /analytics 发送当前网站资源路径的请求,主要用于记录点击次数。
构建 payload:
1 | |
最终 URL:
1 | |
实验二十九:反射型 XSS(严格 CSP + 悬空标记)
提示: 页面存在反射型 XSS 漏洞,且受严格 CSP 保护,无法通过外部子资源直接完成常规利用。可通过构造悬空标记并结合表单劫持方式,诱导受害者点击包含 “Click” 的利用向量,窃取页面中的 CSRF Token,再利用该 Token 发起修改邮箱请求,将邮箱地址更改为 hacker@evil-user.net。
构建 Payload:
1 | |
实验三十:反射型 XSS(CSP 绕过)
提示: 页面存在反射型 XSS 漏洞,并启用了 CSP。虽然常规内联脚本会被策略阻止,但可通过可控的 CSP 参数注入新的策略指令,覆盖原有脚本限制,从而执行内联脚本并调用 alert()。该实验的预期解法仅适用于 Chrome 浏览器。
服务器用户提交的 token 写入了响应头 Content-Security-Policy 中,可注入一个新的 CSP 绕过限制。

构建 Payload:
1 | |

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