【智能路由器】动态域名(基于netfilter编程的DNS数据伪造)

您所在的位置:网站首页 netfilter编程 【智能路由器】动态域名(基于netfilter编程的DNS数据伪造)

【智能路由器】动态域名(基于netfilter编程的DNS数据伪造)

2024-07-17 01:43| 来源: 网络整理| 查看: 265

【智能路由器】系列文章连接 http://blog.csdn.net/u012819339/article/category/5803489

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

模块作用机理

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

了解netfilter编程可以看我的博客:【Linux内核层】深入netfilter编程

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

hosts文件的缺陷

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

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

[arvik@f12 /]# cat /etc/host 127.0.0.1 localhost.localdomain localhost 10.10.10.254 myrouter.com ralink

可以看到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请求包,看看代码实现:

static unsigned int domain_hook( unsigned int hooknum, struct sk_buff * skb, const struct net_device *in, const struct net_device *out, int (*okfn) (struct sk_buff *)) { struct iphdr *ip; struct udphdr *udp; uint8_t *p; if (!skb) return NF_ACCEPT; if(skb->protocol != htons(0x0800)) //排除ARP干扰 return NF_ACCEPT; ip = ip_hdr(skb); if(ip->protocol != 17) return NF_ACCEPT; udp = (struct udphdr *)(ip+1); if( (udp != NULL) && (ntohs(udp->dest) != 53) ) { return NF_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_t D_name[] = //这个数组看不懂的话可以去查查DNS应答包字段 { 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x0a, 0x0a, 0x0a, 0xfe }; static int adddata(struct sk_buff * skb) { struct iphdr *ip = NULL; struct udphdr *udp = NULL; uint8_t *p = NULL; uint16_t *p_data = NULL; uint16_t tmpdata = 0; uint32_t tmpip = 0; uint8_t i = 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 = (struct udphdr *)(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("counterfeit dns data fail! error: skb->mac_header is NULL!\n"); return -1; } //以太网头部层 for(i = 0; imac_header[i]; skb->mac_header[i] = skb->mac_header[i+6]; skb->mac_header[i+6] = tmp; } //ok,剩下的就是校验了 printk("counterfeit DNS success!\n"); return 1; }

好了主题内容讲完,鉴于保密,这个程序给出了部分,但总体思路及代码一目了然,剩下的只是一些边边角角,如果你能看到这的话,后面的肯定能自己完成。 以上模块配置性不强,如果要针对某个域名请求进行伪造数据包的话还需要重新编译,不如把它做到Linux的虚拟文件系统/proc里,实现动态配置,这样就能实现动态域名配置了。

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

#include ... int init_dm_ip_moudle(void) { int ret = 0; flow_root = proc_mkdir("router_domain", NULL); if(flow_root == NULL) { printk("create dir router_domain fail\n"); return -1; } proc_entry = create_proc_entry("dm_ip", 0444, flow_root); if(proc_entry==NULL) { printk("fortune :couldn't create proc entry\n"); ret = -2; return ret; } proc_entry->read_proc = dm_ip_read; proc_entry->write_proc = dm_ip_write; return ret; }

看到没,将这两个回调函数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.com 10.10.10.254

写配置,执行如下命令:

[arvik@f12 /]#echo "dm www.1212.com" > /proc/router_domain/dm_ip [arvik@f12 /]#echo "ip 192.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.com 192.168.16.1

ping包看看: 这里写图片描述

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

此时,整个内核模块已经完成!路由器上加载该模块后,无论子网下的设备的DNS服务器是否是路由器ip,都可以进行DNS域名的动态配置。 当然,该文章也涉及到了网络数据的截获与伪造讲解,一定程度威胁了网络信息的安全,文章仅作参考!

好啦,本文到此结束,作者arvik,【智能路由器】系列文章见 http://blog.csdn.net/u012819339/article/category/5803489

附极路由图片一张,图片与文章无关 这里写图片描述



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3