配色: 字号:
【智能路由器】动态域名(基于netfilter编程的DNS数据伪造)
2016-09-22 | 阅:  转:  |  分享 
  
【智能路由器】动态域名(基于netfilter编程的DNS数据伪造)

本文利用netfilter框架,做了一个在路由器上运行的Linux内核模块,该模块能够拦截指定域名解析的请求数据包,并且伪造对应的DNS应答包,送入网络。



模块作用机理



在netfilter的框架的prerouting点,挂接我们的钩子函数,在钩子函数里实现域名的伪造,见下图:



在上图中红色标记处挂接钩子函数,拦截DNS请求包,直接在请求包上做更改,将其变成一个DNS应答包,然后对调数据包中的源信息和目的信息。



hosts文件的缺陷



我们知道在windows和Linux中都有host文件,利用它可以完成域名的静态映射,即将指定域名映射到某个ip上。路由器上dnsmasq在收到子网的DNS请求时,会先查看host文件里是否已经有要解析的域名的映射,如果有就直接将该映射的ip返回给客户,否则就会向上级路由发出请求,这样逐级请求最终获取dns解析数据。



linux平台下,hosts文件在/etc文件夹中,拿我的路由器做个例子:

执行命令:



[arvik@f12/]#cat/etc/host

127.0.0.1localhost.localdomainlocalhost

10.10.10.254myrouter.comralink

1

2

3

可以看到myrouter.com这个域名被映射到了10.10.10.254(路由器lan口ip)上,当你在浏览器地址栏输入myrouter.com时就跟输入10.10.10.254一样能访问到路由器主页。



说到这就算不知道hosts文件的童鞋也大概了解了hosts文件的作用。(补充一个小故事:前些年为了屏蔽优酷,奇艺,乐视等视频网站视频播放前的广告,就有人利用hosts文件将这些网站的广告服务器域名映射到127.0.0.1上,使其域名不能得到正常解析,所以视频前的广告就无法播放)



只不过,要想hosts发挥作用必须有个前提,就是路由器子网内设备的DNS服务器的地址必须填写路由器的ip地址,否则hosts文件形同虚设!



例如:arvik直接自己电脑上的DNS服务器设为8.8.8.8,这样的话,浏览器访问myrouter.com就看不到路由器主页面了。



然而这就是hosts文件的缺陷,所以就有了下面这段流氓代码的产生!



基于netfilter编程的DNS数据伪造



思路分析及代码



假如你对dns数据包还不熟悉的话,没关系,去看看我的文章【以太网数据包】DNS数据包就足够了!



好了,先得截取路由器子网设备的dns请求包,步骤:

1.提取ip数据

2.提取udp数据(DNS数据包是架设在udp协议之上的)

3.提取udp目的端口为53的数据包(这是DNS的请求报文)

以上三步轻松截取dns请求包,看看代码实现:



staticunsignedintdomain_hook(

unsignedinthooknum,

structsk_buffskb,

conststructnet_devicein,

conststructnet_deviceout,

int(okfn)(structsk_buff))

{

structiphdrip;

structudphdrudp;

uint8_tp;



if(!skb)

returnNF_ACCEPT;



if(skb->protocol!=htons(0x0800))//排除ARP干扰

returnNF_ACCEPT;



ip=ip_hdr(skb);

if(ip->protocol!=17)

returnNF_ACCEPT;



udp=(structudphdr)(ip+1);



if((udp!=NULL)&&(ntohs(udp->dest)!=53))

{

returnNF_ACCEPT;

}



...



}

好了,以上代码中的省略号就是我们接下来要做的了。



我们直接将请求报文更改为应答报文再放回netfilter链即可,考虑以下几点:

1.构造应答包数据部分

2.扩充skb缓冲区

3.更改DNS报头相关字段。比如,FLAGS字段,AnswerRRS字段

4.更改UDP报头。对调源端口和目的端口,修改UDP报文长度(UDP校验放到最后和IP校验一起做)

5.更改ip报头。对调源ip和目的ip,更改ip报文总长度…

6.更改以太网报头。对调源mac和目的mac

7.完成数据伪造。进行udp和ip校验

看看代码:



int8_tD_name[]=//这个数组看不懂的话可以去查查DNS应答包字段

{

0xc0,0x0c,0x00,0x01,0x00,0x01,0x00,0x00,

0x00,0x00,0x00,0x04,0x0a,0x0a,0x0a,0xfe

};



staticintadddata(structsk_buffskb)

{

structiphdrip=NULL;

structudphdrudp=NULL;

uint8_tp=NULL;

uint16_tp_data=NULL;

uint16_ttmpdata=0;

uint32_ttmpip=0;

uint8_ti=0,tmp=0;





memcpy(&D_name[sizeof(D_name)-4],localIP,4);//IP



p=skb_put(skb,sizeof(D_name));

if(NULL!=p)

memcpy(p,D_name,sizeof(D_name));



//

ip=ip_hdr(skb);

udp=(structudphdr)(ip+1);



//DNS报文修改

p_data=(uint16_t)(udp+1);

p_data[1]=htons(0x8580);//FLAGS,标准回复

p_data[3]=htons(1);//AuswerRRs



//UDP报文修改(校验放到最后一步)

tmpdata=udp->source;//交换源端口和目的端口

udp->source=udp->dest;

udp->dest=tmpdata;

udp->len=htons(ntohs(udp->len)+sizeof(D_name));//报文长度



//IP层修改

ip->tot_len=htons(ntohs(ip->tot_len)+sizeof(D_name));//报文长度

ip->id=0x0000;//IP标识

ip->frag_off=htons(0x4000);//不分片,无偏移

tmpip=ip->saddr;//交换源IP和目的IP

ip->saddr=ip->daddr;

ip->daddr=tmpip;



if(skb->mac_header==NULL)

{

printk("counterfeitdnsdatafail!error:skb->mac_headerisNULL!\n");

return-1;

}

//以太网头部层

for(i=0;i<6;i++)

{

tmp=skb->mac_header[i];

skb->mac_header[i]=skb->mac_header[i+6];

skb->mac_header[i+6]=tmp;

}

//ok,剩下的就是校验了

printk("counwww.sm136.comterfeitDNSsuccess!\n");

return1;

}

好了主题内容讲完,鉴于保密,这个程序给出了部分,但总体思路及代码一目了然,剩下的只是一些边边角角,如果你能看到这的话,后面的肯定能自己完成。

以上模块配置性不强,如果要针对某个域名请求进行伪造数据包的话还需要重新编译,不如把它做到Linux的虚拟文件系统/proc里,实现动态配置,这样就能实现动态域名配置了。



利用虚拟文件系统/proc特性,写个内黑配置模块应该不是难事:



#include



...



intinit_dm_ip_moudle(void)

{

intret=0;



flow_root=proc_mkdir("router_domain",NULL);

if(flow_root==NULL)

{

printk("createdirrouter_domainfail\n");

return-1;

}



proc_entry=create_proc_entry("dm_ip",0444,flow_root);

if(proc_entry==NULL)

{

printk("fortune:couldn''tcreateprocentry\n");

ret=-2;



returnret;

}

proc_entry->read_proc=dm_ip_read;

proc_entry->write_proc=dm_ip_write;





returnret;

}

看到没,将这两个回调函数dm_ip_read、dm_ip_write填充好就行了,这两个函数分别实现获取配置的域名及ip和设置待映射的域名及ip,模块会在/proc下建立文件夹router_domain,以及在router_domain下建立dm_ip文件。

arvik已经做好,效果如下

执行命令



[arvik@f12/]#cat/proc/router_domain/dm_ip

myrouter.com10.10.10.254

写配置,执行如下命令:



[arvik@f12/]#echo"dmwww.1212.com">/proc/router_domain/dm_ip

[arvik@f12/]#echo"ip192.168.16.1">/proc/router_domain/dm_ip

//以上命令将www.1212.com映射到192.168.16.1上,查看是否配置成功

[arvik@f12/]#cat/proc/router_domain/dm_ip

www.1212.com192.168.16.1

ping包看看:



此时浏览器地址直接访问www.1212.com就可访问路由器页面,在此就不截图了



此时,整个内核模块已经完成!路由器上加载该模块后,无论子网下的设备的DNS服务器是否是路由器ip,都可以进行DNS域名的动态配置。

当然,该文章也涉及到了网络数据的截获与伪造讲解,一定程度威胁了网络信息的安全,文章仅作参考!

献花(0)
+1
(本文系网络学习天...首藏)