赞
踩
安全测试之跨站脚本攻击(XSS)
前言
随着互联网的不断发展,web应用的互动性也越来越强。但正如一个硬币会有两面一样,在用户体验提升的同时安全风险也会跟着有所增加。今天,我们就来讲一讲web渗透中常见的一种攻击方式:XSS攻击。
(一)什么是XSS攻击
先上一段标准解释(摘自百度百科)。
“XSS是跨站脚本攻击(Cross Site Scripting),为不和层叠样式表(Cascading Style Sheets, CSS)的缩写混淆,故将跨站脚本攻击缩写为XSS。恶意攻击者往Web页面里插入恶意Script代码,当用户浏览该页之时,嵌入其中Web里面的Script代码会被执行,从而达到恶意攻击用户的目的。”
相信以上的解释也不难理解,但为了再具体些,这里举一个简单的例子,就是留言板。我们知道留言板通常的任务就是把用户留言的内容展示出来。正常情况下,用户的留言都是正常的语言文字,留言板显示的内容也就没毛病。然而这个时候如果有人不按套路出牌,在留言内容中丢进去一行
那么留言板界面的网页代码就会变成形如以下:
留言板那么这个时候问题就来了,当浏览器解析到用户输入的代码那一行时会发生什么呢?答案很显然,浏览器并不知道这些代码改变了原本程序的意图,会照做弹出一个信息框。就像这样。
(二)XSS的危害
其实归根结底,XSS的攻击方式就是想办法“教唆”用户的浏览器去执行一些这个网页中原本不存在的前端代码。
可问题在于尽管一个信息框突然弹出来并不怎么友好,但也不至于会造成什么真实伤害啊。的确如此,但要说明的是,这里拿信息框说事仅仅是为了举个栗子,真正的黑客攻击在XSS中除非恶作剧,不然是不会在恶意植入代码中写上alert(“say something”)的。
在真正的应用中,XSS攻击可以干的事情还有很多,这里举两个例子。
· 窃取网页浏览中的cookie值
在页浏览中我们常常涉及到用户登录,登录完毕之后服务端会返回一个cookie值。这个cookie值相当于一个令牌,拿着这张令牌就等同于证明了你是某个用户。
如果你的cookie值被窃取,那么攻击者很可能能够直接利用你的这张令牌不用密码就登录你的账户。如果想要通过script脚本获得当前页面的cookie值,通常会用到document.cookie。
试想下如果像空间说说中能够写入xss攻击语句,那岂不是看了你说说的人的号你都可以登录(不过某些厂商的cookie有其他验证措施如:Http-Only保证同一cookie不能被滥用)
· 劫持流现恶意跳转
这个很简单,是在网页中想办法插入一句像这样的语句:
那么所访问的网站就会被跳转到百度的首页。
早在2011年新浪就曾爆出过严重的xss漏洞,导致大量用户自动关注某个微博号并自动转发某条微博。具体各位可以自行百度。
利用与绕过
那xss漏洞很容易被利用吗?那倒也未必。
毕竟在实际应用中web程序往往会通过一些过滤规则来组织代有恶意代码的用户输入被显示。
不过,这里还是可以给大家总结一些常用的xss攻击绕过过滤的一些方法,算是抛砖引玉。(以下的绕过方式皆通过渗透测试平台Web For Pentester 演示)
· 大小写绕过
这个绕过方式的现是因为网站仅仅只过滤了
· 利用过滤后返回语句再次构成攻击语句来绕过
这个字面上不是很好解,用实例来说。
如下图,在这个例子中我们直接敲入script标签发现返回的网页代码中script标签被去除了,但其余的内容并没有改变。
于是我们就可以为
制造一种巧合,让过滤完script标签后的语句中还有script标签(毕竟alert函数还在),像这样: http://192.168.1.102/xss/example3.php?name=
发现问题了吧,这个利用原理在于只过滤了一个script标签。
· 并不是只有script标签才可以插入代码
在这个例子中,我们尝试前面两种方法都没能成功,原因在于script标签已经被完全过滤,但不要方,能植入脚本代码的不止script标签。
例如这里我们用标签做一个示范。
我们利用如下方式:
http://192.168.1.102/xss/example4.php?name=
就可以再次愉快的弹窗。原因很简单,我们指定的图片地址根本不存在也就是一定会发生错误,这时候onerror里面的代码自然就得到了执行。
以下列举几个常用的可插入代码的标签。
当用户鼠标移动时即可运行代码
当用户鼠标在这个块上面时即可运行(可以配合weight等参数将div覆盖页面,鼠标不划过都不行)
类似的还有onclick,这个要点击后才能运行代码,条件相对苛刻,就不再详述。
· 编码脚本代码绕过关键字过滤
有的时候,服务器往往会对码中的关键字(如alert)进行过滤,这个时候我们可以尝试将关键字进行编码后再插入,不过直接显示编码是不能被浏览器执行的,我们可以用另一个语句eval()来实现。eval()会将编码过的语句解码后再执行,简直太贴心了。
例如alert(1)编码过后就是
ale(1)
所以构建出来的攻击语句如下:
http://192.168.1.102/xss/example5.php?name=
· 主动闭合标签实现注入代码
来看这份代码:
乍一看哇!自带scri
标再一看,WTF!填入的内容被放在了变量里! 这个时候就要我们手动闭合掉两个双引号来实现攻击,别忘了,javascript是一个弱类型的编程语言,变量的类型往往并没有明确定义。 思路有了,接下来要做的就简单了,利用语句如下: http://192.168.1.102/xss/example6.php?name=";alert("I amcoming again~");"
效果如图。
回看以下注入完代码的网页代码,
一直都在制造巧合。。
先是闭合引号,然后分号换行,加入代,
· 闭合一个引号,搞定! 组合各种方式
在实际运用中漏洞的利用可能不会这么直观,需我们不断的尝试,甚至组合各种绕过方式来达到目的。
介绍完一些常用的绕过方式,再倒回来讲一下XSS分类,因为下面讲具体的应用时会用到。
(三)XSS分类:
我们一般把xss分为三类:反射型xss,存储型xss,DOM型xss
00001. 反射型xss
反射型xss又被称为非持久型xss。当用户访问一个带有xss攻击代码的url请求的时候,向服务器发送请求,服务器接受请求后处理,并把客户端发送的xss攻击代码返回给客户端,客户端解析这段代码的时候,就有可能遭受xss攻击。
下面是一个典型的反射型xss攻击示例:
· 用户浏览某个网站A,攻击者在这个网站中嵌入了恶意的脚本用于盗取用户的cookie等信息攻击者诱导用户触发xss攻击(比如诱导用户点击非法链接),当用户触发了xss攻击的时候就会将自己的用户信息发送给攻击者攻击者在获取用户的cookie后,就有可能盗用用户的身份信息进行非法操作
2. 存储型xss
存储型xss又被称为持久化的xss,也是最危险的xss攻击方式。一旦攻击成功,就有可能造成大规模的xss攻击,也就是我们通常所说的xss蠕虫。
存储型xss攻击的一般原理是,客户端将带有xss攻击的数据发送给服务器,服务器接收并存储在数据库。当用户下次再访问这个页面的时候,服务器会读取数据库并将之前的xss代码取出发送给浏览器。浏览器解析这段数据的时候,就会遭受xss攻击。
所以,反射型xss攻击一般需要用户手动触发,而存储型xss攻击却是能够自动触发的。一般来说,反射型xss攻击的危害要比存储型xss攻击的危害要小的多。
3. DOM型xss
我们可以通过JavaScript来操作dom树,所以,xss攻击也是能够做到这一点的。dom型xss攻击最大的危害就是改变我们网页的布局。这种类型的xss是不需要和服务器进行交互的,只发生在客户端处理阶段。比如一段xss攻击的代码是:
const div = document.createElement('div')
div.innerText = 'xss攻击的代码'
document.body.appendChild(div)
(四)xss攻击点
知道了xss的基本类型后我们就要了解xss的攻击点一般在哪里?可以这样说,有输入和输出的地方就有可能遭受xss攻击。所以,我们一定要对客户端的输入和服务端的输出做严格的校验,但是这种校验往往是比较复杂的,因为xss攻击的变法太多太多了。下面几个点是需要我们格外注意的。
· html节点的内容:如果一个节点的内容是动态生成的,里面包含了用户的输入信息就有可能遭受xss攻击。
#{content}
上面的content有可能会携带xss攻击,比如:
· html属性:某个节点的属性是通过用户的输入生成的,那这些输入就有可能包含xss攻击的代码。
src的内容可能是:
#" οnerrοr="alert(123)
会造成html的内容如下:
· javascript代码:JavaScript代码中存在后台注入的变量,或者包含用户输入的信息
const content = '#{content}'
content的内容可能是:';alert(123);'
· 富文本:富文本一般是需要保留html的。这些html就有可能造成xss攻击。
实例应用:
1、劫持访问
劫持访问就是在恶意脚本中插入诸如的代码,那么页面就会跳转到百度首页。
劫持访问在持久型和非持久型XSS中都比较常被利用。持久型XSS中劫持访问的危害不用说大家都清楚,但有人会问非持久型XSS中劫持访问有什么作用呢?
很简单,试想下像http://qq.com,http://baidu.com这样的域名下出现非持久型XSS,那么在发送钓鱼链接时就可以通过http://qq.com等域名进行跳转,一般人一看到http://qq.com之类的域名警惕性会下降,也就更容易上当了。
2、盗用cookie实现无密码登录
具体原理上文已经提到,这里做一个具体演示。由于盗取的cookie需要传回给攻击者,因此往往需要一个服务器来接收盗取的cookie,这也就是xss平台的作用了。网上的xss平台很多,但动手搭建一个也不难,建议有条件的自己搭建。
首先登录平台后台获取到js脚本地址为http://127.0.0.1/XSS/template/default.js,所以我们需要做的是把这段代码植入指定页面。
(这里以DVWA渗透测试平台为例)
我们发现网页对于message长度有限制
素看一下。
发现最大长度有限制,但这仅仅是前端的限制,直双
修改成更大的数字即可。再次尝试,没问题,我们已经将脚本植入完毕。
然后就是坐等别的用户访问这个界面。 这时,另一用
gordonb登录并访问了留言界面,那么他的cookie就会被窃取。我们可以从xss平台的后台获取到。
拿到cookie之后要登录他的帐号就好办了。 打开
界面,调出火狐的firebug插件,调至cookie选项卡(注意,如果你的firebug插件没有cookie选项卡,请再安装firecookie插件即可看到)
然后依次点击cookies-create cookie,随后再弹出的界面中填入两个xss平台获取到的cookie,如图
这里注意要把我箭头所指的地方勾上,这是设置cooki
的地方,不然会在设置完下一秒cookie就失效。
完成之后再次刷新页面,发现已经不是之前的登录界面了,而是录
的界面。至此,一个从cookie窃取到利用的过程就已完成。
3、配合csrf攻击完成恶意请求 先简单解释以下csrf攻击
f攻击就是在未经你许可的情况下用你的名义发送恶意请求(比如修改密码,银行转账等),下面演示一个用xss配合csrf修改用户密码的例子。
首先对修改用户密码的界面进行抓包。
发现没有对原密码进行校验。于是一股邪恶的力量油然而生:要是在xss
本中自动提交get请求修改密码的话。。
说干就干,具体插入语句如下:
有人会问,这不是引用脚本吗?其实不然,本质上这还是发起了一起get请求,因此可以直接使用。与上例一样,插入到message中,再坐等上钩。等下一个用户访问该界面时,密码就会被改为123456了。
我们再看下访问该页面时的抓包情况,发现每次访问该页面都发送了更改密码的请求
效果看数据库(密码md5加密)
访问了该页面的用户密码都被更改了。
都说知己知彼方能百战不殆,知道x
· s攻击的原理那么防御的方法也就显而易见了。 首先是过滤。对诸如
我们的html中有:#{content}
这个html中的content是后端通过获取我们的查询字符串后重新渲染到页面上的。如果我们此时访问页面,就会被浏览器拦截掉
我们可以通过在响应头中添加`X-XSS-Protection: 0`来关闭浏览器的自动防御
· html节点内容的防御
对于html节点内容,我们能够通过转义的手段来防御xss攻击。这里我们并不需要转义太多的东西。因为html中识别的是标签,所以,我们通过把''进行转义,就能很好的防御这部分的xss攻击。
const escapeHtml = str => str.replace(//g, '>')
· 转义html的属性
转义html属性的内容的时候,我们需要知道什么字符能够分离html属性?其实我们所常见的html属性的表示方法一般有如下三种:
id = "app"
id = 'app'
id = app
所以,在对这部分内容进行转义的时候可以把",',和空格全给转义了。
const escapeAttr = str => str.replace(/"|'/g, '&quto;').replace(/s/g, ' ')
但是,这里的转义只能防御部分xss攻击的情况,比如下面这种就束手无策了。
="javascript:alert(0)">链接
对于这中形式的防御,我们就需要使用下面防御富文本xss攻击的手段来阻止。
· javascript代码防御
对于js中的内容的防御,只需要将引号转义(不是转义成实体,而是使用转义符)或者将待输入的内容序列化。
const escapeJs = str => str.replace(/"/g, '"').replace(/'/g, "'").replace(//g, '')
上面只能保证大部分情况的安全,最保险的是对数据进行一次序列化;
JSON.stringify(str)
· 富文本防御
富文本其实就是一大段的html,这些html中会包含大量的格式和html标签。对于富文本的防御,我们不能使用转义的方式,因为html我们要原样保留。因此,我们需要使用过滤的方式来防御富文本的xss攻击。
过滤的方式有两种:
1. 按照黑名单进行过滤--过滤掉我们认为不安全的btml属性和标签。
2. 按照白名单过滤--只允许我们定义好的标签和属性存在。
这两种方式,黑名单的实现比较简单,但是稍不留神就可能留下漏洞。白名单实现比较麻烦,需要将html进行完全的解析,然后过滤,再组装成html。
// 黑名单的方式const xssFilter = html => {
// 黑名单的方式过滤 // 过滤script标签 html = html.replace(//g, '')
// 过滤属性值javascript:开头 html = html.replace(/javascript:[^'"]*/g, '')
// 过滤onerror属性 html = html.replace(/onerrors*=s*['"]?[^'"]*['"]/g, '')
// ...其他很多需要过滤的属性,xss的变种太多了 // 这个时候可以使用白名单进行过滤}
// 白名单的方式过滤// 首先需要将html解析成一个树状结构,然后针对这个树遍历元素,判断标签或者属性是否在白名单中,如果不在// 直接干掉。// 可以直接在node端使用cheerio实现上述功能const xssFilter = html => {
const cheerio = require('cheerio')
const $ = cheerio.load(html)
const whiteList = {
// 可以添加更多的规则 'img': ['src'],
}
$('*').each((index, element) => {
// 不在白名单中 if (!whiteList[element.name]) {
$(element).remove()
return;
}
// 在白名单中,处理属性 for (let attr in element.attribs) {
if (whiteList[element.name].indexOf(attr) === -1) {
// 不在白名单中的属性,移除 $(element).attr(attr, null)
}
}
})
return $.html()}
上面所做的一系列措施都是从开发的角度去做的。然是xss攻击千变万化,要想彻底的杜绝他们,仅仅依靠编程的hack方法是十分麻烦的。所以,浏览器就提出了一个从根本上解决xss攻击的方法:CSP。
csp的全称是内容安全策略。是Content Security Policy的简称。
csp的策略和上面我们提到的白名单策略比较相似,开发者要明确的告诉客户端哪些脚本可以被执行。它的解析和执行都是由客户端决定的,而开发者所需要做的就是告诉浏览器可执行的脚本规则即可。
为了启用csp,我们可以从两个方面进行设置:
· 服务端,设置`Content-Security-Policy`响应头
· 客户端,设置meta标签
="Content-Security-Policy" content="script-src 'self'; object-src 'none'; style-src cdn.example.org third-party.org; child-src https:">
csp的限制策略一般是如下格式:`限制选项 选项值`。
csp提供了很多的限制选项,其实就是设置了加载资源的时候需要采取的策略。对于csp,其加载策略主要有如下几种:
· `script-src`:加载脚本的时候采取的策略
· `style-src`:加载样式表的时候采取的策略
· `img-src`:加载图像的时候采取的策略
· `media-src`:加载媒体文件(音频和视频)采取的策略
· `font-src`:加载字体采取的策略
· `object-src`:加载插件(如flash)采取的策略
· `child-src`:加载框架采取的策略
· `frame-ancestors`:嵌入的外部资源(比如、
· `connect-src`:进行http连接时候的限制策略(通过 XHR、WebSockets、EventSource等)
· `worker-src`:加载worker脚本的时候采取的策略
· `manifest-src`:加载manifest文件时候采取的策略。
上面的限制选项几乎涉及了浏览器加载资源的方方面面,除了上述限制,还有一个`default-src`限制。这个限制是用来设置上面各个选项的默认值的。比如:
Content-Security-Policy: default-src 'self'
上面的这个请求头限制了所有的外部资源必须和当前域名同域。
明确了限制选项,我们可以设置如下限制选项值。这些值其实就是我们对浏览器设置的白名单。
我们可以明确资源加载的域名,如:`example.org`,`https://example.com`
我们可以明确资源加载的路径,如:`example.org/resources/js`
我们可以通过通配符来设置限制,如:`*.example.org`,表示任意的子域名都是可以的
我们可以明确加载的协议,http或者https
我们可以通过关键字self指定,表示当前域名
我们可以通过关键字none指定,表示禁止加载任何的外部资源
上面的这些选项值可以同时指定多个,需要使用空格分开。
Content-Security-Policy: script-src 'self' https://apis.google.com
对于xss的防御,我们可以通过设置`script-src`和`object-src`来限制,同时`script-src`也包含了一些特殊的选项值。
· unsafe-inline:允许执行页面内嵌的script和事件监听函数
· unsafe-eval:允许将字符串代码执行,比如使用eval、setTimeout、setInterval和Function等函数。
· nonce值:每次http响应都会给客户端发送一个token,页面内嵌的脚本必须有这个token才会被允许执行
比如:Content-Security-Policy: script-src 'nonce-EDNnf03nceIOfn39fn3e9h3sdfa'
// 只有明确了这个nonce值才会被执行
· hash值:列出允许执行的脚本代码的Hash值,页面内嵌脚本的哈希值只有吻合的情况下,才能执行。
// 指定hash值
Content-Security-Policy: script-src 'sha256-qznLcsROx4GACP2dm0UCKCzCG-HiZ1guq6ZZDob_Tng='
只有script标签中内容的hash和指定的相同时才会执行
所以,只要我们能设置好csp的安全规则,就能从根本上避免xss攻击。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。