专家-XSS上下文-JavaScript URL中-反射型XSS-某些字符被阻止
# 实验室:XSS上下文-JavaScript URL中的反射型XSS-某些字符被阻止
# 题目
此实验室在 JavaScript URL 中反馈了你的输入,但并非所有内容都像看起来的那样。第一次接触时,这似乎是一个微不足道的挑战;但是,应用程序正在阻止某些字符,以防止 XSS 攻击。
若要解决实验室问题,请执行跨站脚本攻击,该攻击在alert
消息中的某处包含字符串1337
,然后调用alert
函数。
- name: 实验室-专家
desc: XSS上下文-JavaScript URL中的反射型XSS-某些字符被阻止 >>
avatar: https://fastly.statically.io/gh/clincat/blog-imgs@main/vuepress/static/imgs/docs/burpsuite-learn/public/lab-logo.png
link: https://portswigger.net/web-security/cross-site-scripting/contexts/lab-javascript-url-some-characters-blocked
bgColor: '#001350'
textColor: '#d112fe'
2
3
4
5
6
# 实操
点击 “ACCESS THE LAB” 进入实验室。
# 欺骗性“过滤”
一个令人熟悉的博客网站。
随机挑选一篇文章并进入评论区,尝试提交几个评论。
在评论区中并没有发现 XSS 漏洞,那漏洞会在哪儿呢?
在网页源代码中搜索字符时,我发现返回按钮 “Back to Blog” 的href
属性似乎有点奇怪。
<a href="javascript:fetch('/analytics', {method:'post',body:'/post%3fpostId%3d1'}).finally(_ => window.location = '/')">Back to Blog</a>
URL 请求路径被实时反映在了href
属性中,并且通过javascript:
伪协议发起了一个网络请求,最后才跳转并回到网站首页。
点击 “Back to Blog” 并捕获一个请求数据包,令人失望的是,响应数据包中没有实时反映任何东西。
就是一个普通的统计功能。
回到文章,我试图更改postId
参数,看看href
属性会如何变化。
然后......
没关系,我们可以通过&
在 URL 后面添加额外的参数,如图所示。
我们所添加的字符确实被反映在了href
属性当中。
但不幸的是,网站过滤了单引号、等于号、括号、尖括号等字符,路被堵死了。
我试图寻找其他的出口点,但找来找去,只有这一处......
然后我绕啊绕,绕啊绕,还是一直被过滤......
我想不通,于是我瞄了一眼答案......(我是fw)
我悟了,原来是这样。还记得题目中说了什么吗?——此实验室在 JavaScript URL 中反馈了你的输入,但并非所有内容都像看起来的那样。
并非所有内容都像看起来的那样,并非所有内容都像看起来的那样,并非所有内容都像看起来的那样,并非所有内容都像看起来的那样......
例如,你在 URL 中注入了一个单引号'
,然后你看到它变成了%27
,你以为它被过滤了:
<a href="javascript:fetch('/analytics', {method:'post',body:'/post%3fpostId%3d1&%27'}).finally(_ => window.location = '/')">Back to Blog</a>
但其实没有,单引号没有被过滤,而且成功闭合了字符串,你被视觉效果欺骗了。
# 构造载荷
虽然单引号没有被过滤,但某些字符是真的被过滤了,例如等于号、括号、尖括号等,因此无法进行闭合标签等操作。
但既然位于javascript:
伪协议中,那就直接在其中执行 JavaScript 就好了。
接下来要根据注入的上下文构造载荷,如图所示,我们注入的上下文位于:
根据注入的上下文,先用单引号 和 右花括号闭合字符串:
&'}
用逗号分隔前面的...'}
,中间增加一个{}
作为我们注入 JavaScript 的位置,后面再用一个{'...
优雅地闭合原有的字符:
,{},{'
&'},{},{'
2
3
注意:逗号之间不要有空格,因为空格也被过滤了
图中有空格是为了方便阅读代码。
然后,我在XSS备忘单中找到了这样一个载荷:
throw/a/,Uncaught=1,g=alert,a=URL+0,onerror=eval,/1/g+a[12]+[1337]+a[13]
将其放到中间的{}
中,形成完整的载荷:
&'},{throw/a/,Uncaught=1,g=alert,a=URL+0,onerror=eval,/1/g+a[12]+[1337]+a[13]},{'
以下是一个动图(注入载荷、点击按钮、哎呀报错了?)
换 Chrome 浏览器试试,也报错了,提示/
不能使用。
好家伙,连斜杠也过滤。
后经测试,/
、+
(空格)和;
也都被过滤。
把XSS备忘单中的载荷全部试了一遍,但都不成功。
# 最终
然后我看了下半部分的答案(没错,我之前看答案只看了上半部分,现在下半部分也看了,我是大fw)。
答案给出的载荷是这样的:
- 用
'}
闭合前面的字符串 - 用
{x:'
闭合后面的花括号(里面的数据必须是对象形式,不能是{'
) - 中间定义了一个过程
a
,用throw
产生一个异常以触发onerror
事件,用/**/
替代空格(因为空格被过滤了),用逗号,1
来传递参数(因为括号被过滤了),alert
函数的调用将被传递给onerror
事件 a
被赋值给x
,现在x
是一个函数- 用自定义函数
x
覆盖掉原生对象toString
的值 - 将原生对象
window
与 空字符串 进行拼接,“字符串拼接操作” 将自动触发toString
调用,但其已被我们覆盖,所以最终调用的是函数x
,结果将产生一个警告框 - 通过逗号分隔每个表达式
&'},x=a=>{throw/**/onerror=alert,1},toString=x,window+'',{x:'
将载荷注入 URL 中:
/post?postId=1&'},x=a=>{throw/**/onerror=alert,1},toString=x,window+'',{x:'
注入载荷之后,实验室提示我们已经完成了实验。
虽然题目中要求 alert 要包含数字1337
,但貌似其他数字也可以。
然后点击按钮,成功执行 JavaScript(经测试,以上载荷在 Chrome 浏览器中也可以运行)。
实验完成。