Рыбная ловля в локальной сети - sniffering

       

Пассивный перехват трафика


Локальная сеть уже давно стала синонимом слова Ethernet, а в Ethernet-сетях, построенных по топологии общей шины, каждый испускаемый пакет доставляется всем участникам сети. Сетевая карта на аппаратном уровне анализирует заголовки пакетов и сверяет свой физический адрес (так же называемый MAC-адресом) с адресом, прописанном в Ethernet-заголовке, передавая на IP-уровень только "свои" пакеты.

Для перехвата трафика карту необходимо перевести в неразборчивый (promiscuous) режим, в котором на IP-уровень передается все принятые пакеты. Неразборчивый режим поддерживает подавляющее большинство стандартных карт, провоцируя излишне любопытных пользователей на проникновение в интимную жизнь остальных участников сети.

Переход на витую пару с неинтеллектуальным хабом ничего не меняет– отправляемые пакеты дублируется на каждый выход хаба и грабятся по той же самой схеме. Интеллектуальный хаб, самостоятельно анализирующий заголовки пакетов и доставляющий их только тем узлам, для которых они предназначены, предотвращает пассивный перехват, вынуждая атакующего переходить к активным действиям, разговор о которых нас ждет впереди.

Таким образом, для реализации пассивного сниффера мы должны перевести сетевую карту в неразборчивый режим и создать сырой (raw) сокет, дающий доступ ко всему, что валится на данный IP-интерфейс. Обычные сокеты для этой цели не подходят, поскольку принимают только явно адресованные им пакеты, поступающие на заданный порт. Легальные снифферы чаще всего используют кросс-платформенную библиотеку libcap, однако, настоящие хакеры предпочитают разрабатывать ядро сниффера самостоятельно.

Операционные системы семейства UNIX блокируют прямой доступ к оборудованию с прикладного уровня (так что перепрограммировать сетевую карту просто так не удастся), однако, все же предоставляют специальные рычаги для перевода интерфейса в неразборчивый режим, правда, в различных UNIX'ах эти рычаги сильно неодинаковы, что существенно усложняет нашу задачу.

В состав BSD входит специальный пакетный фильтр (BPF – BSD Packet Filter), поддерживающий гибкую схему выборочного перехвата чужих пакетов и соответствующий устройству /dev/bpf.
Перевод интерфейса в неразборчивый режим осуществляется посредством IOCTL и выглядит приблизительно так: ioctl(fd, BIOCPROMISC, 0), где fd – дескриптор интерфейса, а BIOCPROMISC – управляющий IOCTL-код. В Solaris'е все осуществляется аналогично, не совпадает только IOCTL-код и устройство называется не bpf, а hme. Похожим образом ведет себя и SUNOS, предоставляющая потоковый драйвер псевдоустройства nit, так же называемый краником в сетевом интерфейсе (NIT – Network Interface Tap). В отличии от пакетного фильтра BPF, потоковый фильтр NIC перехватывает только входящие пакеты, позволяя исходящим прошмыгнуть мимо него. К тому же он намного медленнее работает. Иную схему грабежа трафика реализует LINUX, поддерживающая специальные IOCTL-коды для взаимодействия с сетью на уровне драйверов. Просто создайте сырой сокет вызовом socket (PF_PACKET, SOCK_RAW, int protocol)

и переведите связанный с ним интерфейс в неразборчивый режим –ifr.ifr_flags |= IFF_PROMISC; ioctl (s, SIOCGIFFLAGS, ifr), где s – дескриптор сокета, а ifr – интерфейс.

Ниже приводится полностью готовая к употреблению функция подготавливающая сырой сокет к работе с переводом интерфейса в неразборчивый режим и поддерживающая большое количество различных операционных систем, так то: SUN OS, LUNUX, Free BSD, IRIX и Solaris, выдернутая из сниффера, исходный текст которого можно найти по адресу: http://packetstormsecurity.org/sniffers/gdd13.c.

/*========================================================

  Ethernet Packet Sniffer 'GreedyDog' Version 1.30

  The Shadow Penguin Security (http://shadowpenguin.backsection.net)



  Written by UNYUN (unewn4th@usa.net)

#ifdef SUNOS4 /*--------< SUN OS4 >-----------*/

#define       NIT_DEV                    "/dev/nit"                               */

#define       DEFAULT_NIC          "le0"                                    */



#define       CHUNKSIZE            4096                                     */

#endif

#ifdef LINUX   /*--------< LINUX >-------------*/

#define       NIT_DEV                    ""

#define       DEFAULT_NIC          "eth0"                                   */

#define       CHUNKSIZE            32000                                    */

#endif

# ifdef FREEBSD /*--------< FreeBSD >-----------*/

#define       NIT_DEV                    "/dev/bpf"                               */

#define       DEFAULT_NIC          "ed0"                                    */

#define       CHUNKSIZE            32000                                    */

#endif

#ifdef IRIX /*-----------< IRIX >--------------*/

#define       NIT_DEV                    ""

#define       DEFAULT_NIC          ""

#define       CHUNKSIZE            60000                                    */

#define       ETHERHDRPAD          RAW_HDRPAD(sizeof(struct ether_header))

#endif

#ifdef SOLARIS /*--------< Solaris >-----------*/

#define       NIT_DEV                    "/dev/hme"                               */

#define       DEFAULT_NIC          ""

#define       CHUNKSIZE            32768                                    */

#endif

#define       S_DEBUG                                                              */

#define       SIZE_OF_ETHHDR             14                                       */

#define       LOGFILE                    "./snif.log"                             */

#define       TMPLOG_DIR           "/tmp/"                                         */

struct conn_list{

       struct conn_list     *next_p;

       char                 sourceIP[16],destIP[16];

       unsigned long        sourcePort,destPort;

};

struct conn_list *cl; struct conn_list *org_cl;

#ifdef SOLARIS

       int    strgetmsg(fd, ctlp, flagsp, caller)



       int    fd;

       struct strbuf  *ctlp;

       int    *flagsp;

       char   *caller;

       {

              int    rc;

              static char errmsg[80];

             

              *flagsp = 0;

              if ((rc=getmsg(fd,ctlp,NULL,flagsp))<0) return(-2);

              if (alarm(0)<0) return(-3);

              if ((rc&(MORECTL|MOREDATA))==(MORECTL|MOREDATA)) return(-4);

              if (rc&MORECTL) return(-5);

              if (rc&MOREDATA) return(-6);

              if (ctlp->len<sizeof(long)) return(-7);

              return(0);

       }

#endif

int    setnic_promisc(nit_dev,nic_name)

char   *nit_dev;

char   *nic_name;

{

       int sock; struct ifreq f;

      

#ifdef SUNOS4

       struct strioctl si; struct timeval timeout;

       u_int chunksize = CHUNKSIZE; u_long if_flags = NI_PROMISC;

      

       if ((sock = open(nit_dev, O_RDONLY)) < 0)              return(-1);

       if (ioctl(sock, I_SRDOPT, (char *)RMSGD) < 0)   return(-2);

       si.ic_timout = INFTIM;

       if (ioctl(sock, I_PUSH, "nbuf") < 0)                   return(-3);

      

       timeout.tv_sec = 1; timeout.tv_usec = 0; si.ic_cmd = NIOCSTIME;

       si.ic_len = sizeof(timeout); si.ic_dp  = (char *)&timeout;

       if (ioctl(sock, I_STR, (char *)&si) < 0) return(-4);

      

       si.ic_cmd = NIOCSCHUNK; si.ic_len = sizeof(chunksize);

       si.ic_dp  = (char *)&chunksize;

       if (ioctl(sock, I_STR, (char *)&si) < 0)        return(-5);

      

       strncpy(f.ifr_name, nic_name, sizeof(f.ifr_name));

       f.ifr_name[sizeof(f.ifr_name) - 1] = '\0'; si.ic_cmd = NIOCBIND;

       si.ic_len = sizeof(f); si.ic_dp  = (char *)&f;

       if (ioctl(sock, I_STR, (char *)&si) < 0)        return(-6);

      

       si.ic_cmd = NIOCSFLAGS; si.ic_len = sizeof(if_flags);

       si.ic_dp  = (char *)&if_flags;

       if (ioctl(sock, I_STR, (char *)&si) < 0)      return(-7);



       if (ioctl(sock, I_FLUSH, (char *)FLUSHR) < 0) return(-8);

#endif

#ifdef LINUX

       if ((sock=socket(AF_INET,SOCK_PACKET,768))<0) return(-1);

       strcpy(f.ifr_name, nic_name);     if (ioctl(sock,SIOCGIFFLAGS,&f)<0) return(-2);

       f.ifr_flags |= IFF_PROMISC;       if (ioctl(sock,SIOCSIFFLAGS,&f)<0) return(-3);

#endif

#ifdef FREEBSD

       char device[12]; int n=0; struct bpf_version bv; unsigned int size;

      

       do{

              sprintf(device,"%s%d",nit_dev,n++); sock=open(device,O_RDONLY);

       } while(sock<0 && errno==EBUSY);

       if(ioctl(sock,BIOCVERSION,(char *)&bv)<0) return(-2);

       if((bv.bv_major!=BPF_MAJOR_VERSION)||(bv.bv_minor<BPF_MINOR_VERSION))return -3;

       strncpy(f.ifr_name,nic_name,sizeof(f.ifr_name));

       if(ioctl(sock,BIOCSETIF,(char *)&f)<0) return-4;

       ioctl(sock,BIOCPROMISC,NULL);if(ioctl(sock,BIOCGBLEN,(char *)&size)<0)return-5;

#endif

#ifdef IRIX

       struct sockaddr_raw sr; struct snoopfilter sf;

       int size=CHUNKSIZE,on=1; char *interface;

       if((sock=socket(PF_RAW,SOCK_RAW,RAWPROTO_SNOOP))<0) return -1;

       sr.sr_family = AF_RAW; sr.sr_port = 0;

       if (!(interface=(char *)getenv("interface")))

       memset(sr.sr_ifname,0,sizeof(sr.sr_ifname));

       else strncpy(sr.sr_ifname,interface,sizeof(sr.sr_ifname));

       if(bind(sock,&sr,sizeof(sr))<0) return(-2); memset((char *)&sf,0,sizeof(sf));

       if(ioctl(sock,SIOCADDSNOOP,&sf)<0) return(-3);

       setsockopt(sock,SOL_SOCKET,SO_RCVBUF,(char *)&size,sizeof(size));

       if(ioctl(sock,SIOCSNOOPING,&on)<0) return(-4);

#endif

#ifdef SOLARIS

       long buf[CHUNKSIZE]; dl_attach_req_t ar; dl_promiscon_req_t pr;

       struct strioctl si; union DL_primitives *dp; dl_bind_req_t bind_req;

       struct strbuf c; int flags;

      

       if ((sock=open(nit_dev,2))<0) return(-1);



      

       ar.dl_primitive=DL_ATTACH_REQ; ar.dl_ppa=0; c.maxlen=0;

       c.len=sizeof(dl_attach_req_t); c.buf=(char *)&ar;

       if (putmsg(sock,&c,NULL,0)<0) return(-2);

      

       c.maxlen=CHUNKSIZE; c.len=0; c.buf=(void *)buf;

       strgetmsg(sock,&c,&flags,"dlokack"); dp=(union DL_primitives *)c.buf;

       if (dp->dl_primitive != DL_OK_ACK) return(-3);

      

       pr.dl_primitive=DL_PROMISCON_REQ; pr.dl_level=DL_PROMISC_PHYS; c.maxlen = 0;

       c.len=sizeof(dl_promiscon_req_t); c.buf=(char *)&pr;

       if (putmsg(sock,&c,NULL,0)<0) return(-4);

      

       c.maxlen=CHUNKSIZE; c.len=0; c.buf=(void *)buf;

       strgetmsg(sock,&c,&flags,"dlokack"); dp=(union DL_primitives *)c.buf;

       if (dp->dl_primitive != DL_OK_ACK) return(-5);

      

       bind_req.dl_primitive=DL_BIND_REQ; bind_req.dl_sap=0x800;

       bind_req.dl_max_conind=0; bind_req.dl_service_mode=DL_CLDLS;

       bind_req.dl_conn_mgmt=0; bind_req.dl_xidtest_flg=0; c.maxlen=0;

       c.len=sizeof(dl_bind_req_t); c.buf=(char *)&bind_req;

       if (putmsg(sock,&c,NULL,0)<0) return(-6);

      

       c.maxlen=CHUNKSIZE; c.len=0; c.buf=(void *)buf;

       strgetmsg(sock,&c,&flags,"dlbindack"); dp=(union DL_primitives *)c.buf;

       if (dp->dl_primitive != DL_BIND_ACK) return(-7);

      

       si.ic_cmd=DLIOCRAW; si.ic_timout=-1; si.ic_len=0; si.ic_dp=NULL;

       if (ioctl(sock, I_STR, &si)<0) return(-8);

       if (ioctl(sock,I_FLUSH,FLUSHR)<0) return(-9);

#endif

       return(sock);

}

Листинг 1  создание сырого сокета (дескриптора) с переводом интерфейса в неразборчивый режим



Рисунок 3 сниффер за работой


Содержание раздела