现代前端框架的XSS探寻
前言
如今大量前端开发皆采用现代化前端框架,如Vue和React,这类框架对于XSS提供了自带的防御措施,大部分情况下基本完全杜绝了XSS,但是在某些特定的场合下,依旧存在XSS风险,为了避免在一些渗透测试,安全服务中在这类框架上花无用的挖洞时间,此文来探讨一下此类框架是如何防御XSS以及如何产生XSS的
介于此,此文将尽可能少的介绍Vue和React的基础知识,不了解的读者可以视情况先学习一下前置知识在看此文
Vue
防御
我们知道,Vue采用数据绑定的方式,将变量绑定到dom树上,如下所示
<div id="app">
<p>{{ message }}</p>
</div>
<script>
new Vue({
el: '#app',
data: {
message: 'Hello Vue.js!'
}
})
</script>
el代表dom的查询表达式,查询id=app的元素
然后将 {{}}里的message变量渲染为Hello Vue.js! 字符串
几乎所有的开发都这么写,那么假如我们message变量可控会如何
<div id="app">
<p>{{ message }}</p>
</div>
<script>
new Vue({
el: '#app',
data: {
message: '<svg/onload=alert(1)>'
}
})
</script>
这里message被我们硬编码为了html代码,当然正常代码会动态给message赋值,这里效果一样,不额外举例,现在我们看看渲染的结果
可以看到,即使我们可控这些变量,开发者也没有做任何过滤,我们的xss payload依旧被转义为实体字符。
这就是vue底层做的事,vue对于变量绑定,底层其实是用了Javascript原生的innerText方法,而innerText默认会把所有html元素进行转义,由于innerText也是Js原生方法,当然正常情况不可能绕过
所以有时候,我们在某些系统里狂插payload,大家会发现没有一个弹窗,可能这不是开发者防御意识高,只是人家用了框架,攻击的方式错了
隐患1
如果开发者有将html代码作为值传入到变量中的需求呢,比如动态生成表格,对于一个大型框架来说,肯定有这种渲染标签的功能,
对于Vue来说,这个功能的指令叫V-html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Vue</title>
<script src="https://cdn.staticfile.org/vue/2.2.2/vue.min.js"></script>
</head>
<body>
<div id="app">
<p v-html="message"></p>
</div>
<script>
new Vue({
el: '#app',
data: {
message: '<svg/onload=alert(1)>'
}
})
</script>
</body>
</html>
很明显感觉到,v-html指令应该就是调用的document.innerHTML方法
如果开发者用v-html方法进行参数传递,并且参数可控,那么即可造成XSS
隐患2
vue用的最多的方法还是{{}},双大括号变量绑定,这个双大括号本质和 v-text指令是一样的,就是上面说的调用的innerText方法,对于一般的普通系统来说,几乎开发者全程使用{{}}进行参数传递,很少会额外用到v-html指令,那么如果不用v-html就没有常见的XSS问题了嘛?
对于一种很常见的XSS case,vue是没有做任何的防御的
<body>
<div id="app">
<a :href="url">click me</a>
</div>
<script>
new Vue({
el: '#app',
data: {
url: "javascript:alert(111)"
}
})
</script>
</body>
a标签的属性xss,vue是没有任何防御措施的,我们只需要传入javascript:alert(1)即可触发XSS
除此以外
iframe的src属性,object标签的src属性都是可以触发XSS的
:href 其实是 v-bind:href的缩写
下面两个写法一样
<a :href="url">click me</a>
<a v-bind:href="url">click me</a>
隐患3
在javascript函数里面使用原生危险函数,比如innerHTML eval等,这个不再多说
React
隐患1
跟上诉href src一样,react不会对链接产生的xss进行过滤
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>Hello React!</title>
<script src="https://cdn.staticfile.org/react/16.4.0/umd/react.development.js"></script>
<script src="https://cdn.staticfile.org/react-dom/16.4.0/umd/react-dom.development.js"></script>
<script src="https://cdn.staticfile.org/babel-standalone/6.26.0/babel.min.js"></script>
</head>
<body>
<div id="example"></div>
<script type="text/babel">
class WebSite extends React.Component {
constructor() {
super();
}
render() {
return (
<div>
<a href={this.props.site}>click me</a>
</div>
);
}
}
ReactDOM.render(
<WebSite site="javascript:alert(1)"/>,
document.getElementById('example')
);
</script>
</body>
</html>
隐患2
同样如上v-html,react也提供了一个渲染html的方法,不过这个办法名字比较吓人
dangerouslySetInnerHTML
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>Hello React!</title>
<script src="https://cdn.staticfile.org/react/16.4.0/umd/react.development.js"></script>
<script src="https://cdn.staticfile.org/react-dom/16.4.0/umd/react-dom.development.js"></script>
<script src="https://cdn.staticfile.org/babel-standalone/6.26.0/babel.min.js"></script>
</head>
<body>
<div id="example"></div>
<script type="text/babel">
class WebSite extends React.Component {
constructor() {
super();
}
render() {
return (
<div>
<p dangerouslySetInnerHTML={{__html:this.props.site}}></p>
</div>
);
}
}
ReactDOM.render(
<WebSite site="<svg/onload=alert(1)>"/>,
document.getElementById('example')
);
</script>
</body>
</html>
这个dangerouslySetInnerHTML跟vue的v-html比较类似
隐患3
同上,使用原生函数
总结
白盒审计
如果我们在对前端项目进行审核的时候,如果发现前端项目是用vue或者react进行编写的话,我们就可以只关注上面的危险函数关键字,避开其他的常规功能,比如全局搜索 如下关键字
:href
v-bind:href
:src
v-bind:src
v-html
------------------------------
href={
src={
dangerouslySetInnerHTML=
------------------------------
innerHTML
eval(
....
黑盒审计
当然,大部分时候我们是没有源代码的,我们一般面对的是一个webpack打包后的构建产物js,如果有sourcemap泄露的话,我们就可以当上面白盒进行审计,如果在没有sourcemap泄露的情况下,就必须硬怼黑盒环境了,当然黑盒会有一些技巧
一般来说,Vue和React不会单独使用,大部分与现在前端常用的UI框架结合使用,比如Vue+elementUI,React+Antdesign
这类UI框架有个很明显的特征,一般常用于CRM系统,就是什么后台管理系统这类的,像这个样子
这种清一色的,左边上面菜单栏,中间内容,UI比较爽快的,大部分都是Vue+React的前端框架,所以在这里面疯狂扔<img src=#....是没有用的
正确的姿势是:
找输入链接的地方,比如网址,图片链接等输入框,插入javascript:alert(1)等,当然这里也有可能SSRF
找富文本,表格等,或者抓包发现往后端直接发送标签的地方,插入XSS poc
放弃掉其他功能,因为框架会做默认的转义
评论测试
评论测试
评论测试2
评论测试3
太强了,学到了很多
太强了,学到了很多
太强了,学到了很多