浅谈ARP欺骗

来源:互联网 发布:程序员 判刑 编辑:程序博客网 时间:2024/06/05 16:26

 ARP协议(地址解析协议)能实现任意网络层地址到任意物理地址的转换,不过在此处我们只讨论从IP地址到以太网地址(MAC地址)的转换。 ARP协议属于TCP/IP协议栈中的数据链路层协议。所以它只有两层数据包头——以太网头、ARP头。





ARP攻击的原理

        ARP协议是非常天真的协议,PC1喊一句:谁的IP地址192.168.31.1,快点把MAC地址发给我;如果PC2拥有这个IP地址则回应ARP请求。但是这只是理想情况,事实上一个没有拥有IP地址192.168.31.1的机器也是可以发送回应包的。如果PC1信以为真那么就会拿到错误的“IP地址<->MAC地址”关系。这就是ARP攻击。

        从应用层面看机器之间通讯是基于IP地址的,但是操作系统在发送数据包的时候会在IP头部加上以太网头。这是由于以太网规范所决定的——数据包在网络上传送使用的地址都是物理地址。错误“IP<->MAC映射”会导致“数据包不可达”——通俗的说“连不上对方”。如果这个IP地址刚好是网关,那就意味着——“断网”。

实施ARP攻击需要解决三个问题

  • 探测到ARP请求
  • 发送ARP回应数据包
  • 请求者“应用”我们的回应包

探测ARP请求

        以太网是“共享传输介质”,所以任何在网络上传递的数据包都可以被所有网卡探测到。为了减轻网卡的压力网卡设计的时候会只选择读取和自己MAC地址匹配的数据包(以太网数据包头目标地址=自己MAC地址)所以操作系统是无法读取“所有数据包”的。当然,这个开关是可以被关闭的——这就是混杂模式。在这种模式下所有的数据包都会被网卡接受并且传递到操作系统的TCP/IP协议栈。

        ARP请求本身就是广播数据包所以我们打不打开"混杂模式"都可以探测到ARP请求。而实现网络探测我们使用libpcap库实现,关于libpcap库在本章其他笔记里有详细介绍。

        使用libpcap库中的pcap_open_live函数打开设备进行嗅探。使用pcap_loop函数嗅探数据包。pcap_loop第一个参数是pcap_open_live的返回值,它是一个结构体;第二个参数是抓取次数,-1表示一直抓取;callback回调函数地址,每当抓取到数据包都会调用该函数 ;最后一个参数是回调函数的参数。

 int pcap_loop(pcap_t *p, int cnt, pcap_handler callback, u_char *user)

回调函数的原型:
   void got_packet(u_char *args, const struct pcap_pkthdr *header, const u_char *packet);
让我们更细致的考察它。第一个参数相应于pcap_loop()的最后一个参数。每当回调函数被调用时,无论最后一个参数传给pcap_loop()什么值,这个值都会传给我们回调函数的第一个参数。第二个参数是pcap头文件定义的,它包括数据包被嗅探的时间、大小等信息。结构体pcap_pkhdr在pcap.h中定义如下:
   struct pcap_pkthdr {
   struct timeval ts; /* 时间戳 */
   bpf_u_int32 caplen; /* 已捕捉部分的长度 */
   bpf_u_int32 len; /* 该包的脱机长度 */
   };
这些量都相当明了。最后一个参数在它们中是最有意思的,也最让pcap程序新手感到迷惑。这又是一个u_char指针,它包含了被pcap_loop()嗅探到的所有包。但是你怎样使用这个我们在原型里称为packet的变量呢?一个数据包包含许多属性,因此你可以想象它不只是一个字符串,而实质上是一个结构体的集合(比如,一个TCP/IP包会有一个以太网的头部,一个IP头部,一个TCP头部,还有此包的有效载荷)。这个u_char就是这些结构体的串联版本。为了使用它,我们必须作一些有趣的匹配工作。

packet表示完整的数据包,所以它的开头一定是以太网数据包头。

我们只需要判断太网帧的类型字段就知道这是不是一个ARP请求包。然后跳过以太网头一定是ARP头部。如下:

 
if (ntohs(ether_hdr->ether_type) == ETHERTYPE_ARP)
 {  //以太网帧的类型字段为0X806,即表示这是一个ARP请求数据包
 //获取ARP数据包头部地址---以太网帧帧首偏移一个以太网帧帧头的长度(14子节)
        arphdr_t *arp_hdr = (arphdr_t *) (packet + LIBNET_ETH_H);
 ......
}

        获取了ARP头部后我们就可以解析出ARP请求包中的源IP、源MAC地址等相关信息用于构建arp应答包了。


发送ARP回应数据包

        我们使用libetnet来完成这个工作。libetnet库的相关介绍在另一篇笔记里也有详尽的介绍。这里就直说一下大体操作过程。

        构造一个ARPOP_REPLY类型的数据包(回应);mac_addr是自己的MAC地址;tpa是请求arp的目标地址(对于我们来说是源地址——即便我们不是192.168.31.1也是可以填写这个字段,系统不会校验这部分数据);后面的数据都可以通过以太网头和arp头填充。

最后加上以太网头然后就可以发送了。


请求者“应用”的回应包

        PC1发送ARP请求,如果被我们的程序探测到并且我们“编造”一个ARP回应那么对于PC1来说它会收到两次ARP回应。一条是正确的ARP回应,一条是我们编造的ARP回应。 对于Windows来说它会选择最后到达的回应应用;对于Linux来说它会选择第一个到达的回应应用。(所以一些简单的ARP攻击Linux是天生免疫的,此处你可以尽情吐槽一下Windows的弱智)所以如果我们要攻击Windows只需要sleep(1)再发送回应就行了;对于Linux来说就比较困难了——我们很难保证自己的数据包会优先到达。但是正所谓道高一次魔高一丈,我们不能欺骗Linux主机但是可以冒充Linux主机啊。简单来说就是:当网关发送ARP请求探测PC的时候我们可以冒充PC回应网关的请求,这也会到导致——断网。

ARP防护

        ARP的防护办法其实很简单,以360的流量防火墙为例。它启动的时候会首先拿到当前网关的MAC地址,通过修改系统底层所有的ARP回应数据包都不会被真正的应用,这样就实现了“防”(你可以用MAC+IP地址绑定的方式固定网关,但是显的不够高大上)。当收到ARP回应的时候360会比较自己保存的网关MAC地址,如果发现MAC地址发生了变化就认为发现了ARP攻击。最最致命的——无论你怎么伪造你都会暴漏自己的mac地址(回应ARP的时候头部以太网头的源地址是你自己的MAC地址)这是由于网卡本身的限制,如果mac地址不是自己的数据包是“发不出去的”。

原创粉丝点击