Пассивный перехват трафика
Локальная сеть уже давно стала синонимом слова 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 *)≺
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 сниффер за работой