Sniffer exploit или пассивное сканирование
При желании червь может перехватывать весь трафик, проходящий через уязвимый узел, а не только тот, что адресован атакованному сервису. Такое пассивное прослушивание чрезвычайно трудно обнаружить и, из всех рассмотренных нами способов обхода брандмауэров, этот обеспечивает червю наивысшую скрытность.
По сети путешествует огромное количество снифферов для UNIX, в большинство своем распространяемых в исходных текстах и неплохо прокомментированных. Наиболее универсальный способ прослушивания трафика апеллирует к кросс-платформенной библиотеке libcap, портированной в том числе и под Windows95/98/ME/NT/2000/XP/CE (на сайте http://winpcap.polito.it/install/default.htm можно найти и сам порт библиотеки, и tcpdump для Windows – очень рекомендую). Если же на атакованном компьютере этой библиотеки нет (а червь не может позволить себе роскошь тащить ее за собой), мы будем действовать так: открываем сокет в сыром режиме, связываем его с прослушиваемым интерфейсом, переводим последний в "неразборчивый" (promiscuous) режим, в котором сокет будет получать все, проходящие мимо него пакеты, и… собственно, читаем их в свое удовольствие.
В различных операционных системах этот механизм реализуется по-разному. В LINUX начиная с версии 2.2 появились поддержка пакетных сокетов, предназначенных для взаимодействия с сетью на уровне драйверов и создаваемых вызовом socket (PF_PACKET, int type, int protocol), где type может принимать значения SOCK_RAW
("сырой" сокет) или SOCK_DGRAM ("сухой" сокет с удаленными служебными заголовками). Вызов ifr.ifr_flags |= IFF_PROMISC; ioctl (s, SIOCGIFFLAGS, ifr) активирует неразборчивый режим, где ifr – интерфейс, к которому сокет был привязан сразу после его создания (подробнее об этом можно прочитать в статье "анализатор сетевого трафика", опубликованной в октябрьском номере журнала "Системный Администратор" за 2002 год).
Под BSD можно открыть устройство "/dev/bpf" и после его перевода в неразборчивый режим – ioctl(fd, BIOCPROMISC, 0) – слушать пролетающий мимо узла трафик.
В Solaris' е все осуществляется аналогично, только IOCTL-коды немного другие и устройство называется не bpf, а hme. Аналогичным образом ведет себя и SUNOS, где для достижения желаемого результата приходится отрывать устройство nit.
Короче говоря, эта тема выжата досуха и никому уже не интересна. Замечательное руководство по программированию снифферов (правда, на французском языке) можно найти на http://www.security-labs.org/index.php3?page=135, а на http://packetstormsecurity.org/sniffers/
выложено множество разнообразных "грабителей" трафика в исходных текстах. Наконец, по адресам http://athena.vvsu.ru/infonets/Docs/sniffer.txt и http://cvalka.net/read.php?file=32&dir=programming
вас ждет пара толковых статей о снифферах на русском языке. Словом, на недостаток информации жаловаться не приходится. Правда, остается неявным – как примерить весь этот зоопарк и удержать в голове специфические особенности каждой из операционных систем?
Подавляющее большинство статей, с которыми мне приходилось встречаться, описывали лишь одну, ну максим две операционные системы, совершенно игнорируя существование остальных. Приходилось открывать несколько статей одновременно и попеременно мотаться между ними на предмет выяснения: а под LINUX это как? А под BSD? А под Solaris? От этого в голове образовывался такой кавардак, что от прочитанного материала не оставалась и следа, а компилируемый код содержал огромное количество фатальных ошибок, даже и не пытаясь компилироваться.
Чтобы не мучить читателя сводными таблицами (от которых больше вреда, чем пользы), ниже приводится вполне работоспособная функция абстракции, подготавливающая сокет (дескриптор устройства) к работе и поддерживающая большое количество различных операционных систем, так то: 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);
}