/* * lame LIM auditor * * catch and check for proper windows like MAILSLOT (UDP, port 138) traffic * programs which generate such traffic - http://dmess.ytu.ru/rus/winpopups.shtml * or see http://rootshell.ru/~mator/llim/lim_v21/ as example (windows version) * for linux version see http://ksalup.sf.net * * Anatoly Pugachev (c) 2005 * Released under GPL v2 * * needed libs: libpcap and librecode * * compile with: gcc -lpcap -lrecode -o llim llim.c * * * 24.05.05: * - project started * 26.05.05: * - message logging * - idea about making server from this and use client to send messages * this will make server to use libnet to inject da packetz! * - strange packets still sometimes appear * 29.05.05: * - privilege drop to nobody * 30.05.05 * - command line switch -b = messages shown only for broadcasts * - ничего неправильно для auditor'a, надо делать на основе запроса * к WINS серверу, а не на основе опроса dns * в свою очередь для samba wins сервера уже сделать dns proxy = yes * 03.06.05 01:11 < wart_home> кто-то тут показывал ссылку на начало аццкого трэшака с восставшими фошшистами. 01:12 < wart_home> mator: Нет. Тогда тебе надо говорить setlocale(LC_ALL, ""), потом char *s = setlocale(LC_ALL, NULL) и парсить уже эту строку на предмет кодировки. 01:13 < mator> http://rootshell.ru/~mator/some.c 01:13 < mator> wart_home, попробую 01:14 < wart_home> mator: Зануда ты, вот что. Давно бы уж сам всё прочёл :) * * TODO: * - поддержка CHROOT и syslog'a * */ /* what to use (preffered) - recode library or iconv ? 26.05.05 - code is based on recode library, since at first i've readed docs on recode iconv is pretty easy too! - please rewrite to use both methods, make check for codebase support on autoconf/automake and fallback to no message recoding if no one methods avail */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include extern int h_errno; #include #include #if HAVE_STDBOOL_H #include #else typedef enum {false = 0, true = 1} bool; #endif #include #if HAVE_ICONV_H #include #endif #define VERSION 0.01 /* packet format is following : 11:37:18.799033 IP 192.168.5.81.netbios-dgm > 192.168.0.255.netbios-dgm: NBT UDP PACKET(138) 0x0000: 4500 00ee 555e 0000 7f11 5e00 c0a8 0551 E...U^....^....Q 0x0010: c0a8 00ff 008a 008a 00da 0c74 110e 8091 ...........t.... 0x0020: c0a8 0551 008a 00c4 0000 2045 4d45 4a45 ...Q.......EMEJE 0x0030: 4845 4946 4545 4f45 4a45 4f45 4843 4143 HEIFEEOEJEOEHCAC 0x0040: 4143 4143 4143 4143 4141 4100 2045 4945 ACACACACAAA..EIE 0x0050: 5046 4446 4545 4645 4d43 4444 4543 4143 PFDFEEFEMCDDECAC 0x0060: 4143 4143 4143 4143 4143 4141 4100 ff53 ACACACACACAAA..S 0x0070: 4d42 2500 0000 0018 0400 0000 0000 0000 MB%............. 0x0080: 0000 0000 0000 0000 fffe 0000 0000 1100 ................ 0x0090: 0028 0002 0000 0000 0002 0000 0000 0000 .(.............. 0x00a0: 0000 0058 0028 0058 0003 0001 0000 0002 ...X.(.X........ 0x00b0: 003b 005c 4d41 494c 534c 4f54 5c6d 6573 .;.\MAILSLOT\mes 0x00c0: 736e 6772 0000 4c49 4748 544e 494e 4700 sngr..LIGHTNING. 0x00d0: 2a00 a8ab a820 e1a5 aaa8 adec 20a2 20ad *............... 0x00e0: a5e2 e0a0 a9e2 20aa ae20 acad a500 .............. see LIGHTNING in packet, SHE IS A WINNER! hostel#5 @ saratov */ #define lim_bpf_prg "udp[146:4] = 0x00580003 and port 138" #define FIRST_CH 177 /* shift of first 'X' to check, offset 0xa3 */ #define SECOND_CH (FIRST_CH + 4) /* second 0x58 to check, offset 0xa7 */ #define THIRD_CH (SECOND_CH + 2) /* third check for 0x03 */ #define MAILSLOT_SHIFT (FIRST_CH + 16) /* processing starts from calculating mailslot name skipping two zero bytes name from which message received + \0 then name to which message is sent + \0 and finally message itself + \0 */ #define mailslot_lim "\\MAILSLOT\\messngr" #define MAX_NAMELEN 20 #define MAX_MESSAGESIZE 390 char *nobody = "nobody"; /* Whether to display verbose messages. */ int verbose = 0; /* show broadcast messages */ int show_broadcast = 0; RECODE_REQUEST request; char *device = NULL; char *address = NULL; bpf_u_int32 maskp; /* subnet mask */ bpf_u_int32 netp; /* ip */ bpf_u_int32 brdcastp; /* broadcast */ struct t_netnames_list { char name[MAX_NAMELEN]; u_int32_t ip; u_int ttl; struct t_netnames_list *next; struct t_netnames_list *prev; }; struct t_netnames_list *listhead; struct my_ip { u_int8_t ip_vhl; /* header length, version */ #define IP_V(ip) (((ip)->ip_vhl & 0xf0) >> 4) #define IP_HL(ip) ((ip)->ip_vhl & 0x0f) u_int8_t ip_tos; /* type of service */ u_int16_t ip_len; /* total length */ u_int16_t ip_id; /* identification */ u_int16_t ip_off; /* fragment offset field */ #define IP_DF 0x4000 /* dont fragment flag */ #define IP_MF 0x2000 /* more fragments flag */ #define IP_OFFMASK 0x1fff /* mask for fragmenting bits */ u_int8_t ip_ttl; /* time to live */ u_int8_t ip_p; /* protocol */ u_int16_t ip_sum; /* checksum */ struct in_addr ip_src,ip_dst; /* source and dest address */ }; /* The name of this program. */ const char* program_name; void print_usage (FILE* stream, int exit_code) { // fprintf (stream, "Usage: %s options \"bpf filter program\"\n", program_name); fprintf (stream, "Usage: %s options\n", program_name); fprintf (stream, " -h --help Display this usage information.\n" " -i --interface eth1 Catch packets on this interface.\n" " -v --verbose Verbose mode and diagnostic messages.\n" " -b Print only broadcast messages.\n" " -r charset recode messages to charset.\n" " use 'iconv -l' or 'recode -l' for full supported charset listing\n" " defaults to autodetect from LANG env variable.\n"); exit (exit_code); } /* функция возвращает имя компьютера по его ip (network byte order) */ char *real_from(u_int32_t ip) { char *result; struct in_addr umka; struct hostent *hostname; umka.s_addr = ip; hostname = gethostbyaddr(&umka, sizeof(struct in_addr), AF_INET); if (hostname) result = hostname->h_name; else result = inet_ntoa(umka); return result; } /* just print a count every time we have a packet... */ void my_callback(u_char *useless,const struct pcap_pkthdr* pkthdr,const u_char* packet) { static int count = 1; int i; u_char *ptr; /* printing out hardware header info */ struct ether_header *eptr; /* net/ethernet.h */ const struct my_ip* ipp; char from[MAX_NAMELEN+1], to[MAX_NAMELEN+1]; char message[MAX_MESSAGESIZE+1]; fprintf(stdout,"%d: ",count); if (packet) { // fprintf(stdout, "Ethernet address length is %d\n",ETHER_HDR_LEN); /* lets start with the ether header... */ eptr = (struct ether_header *) packet; /* THANK YOU RICHARD STEVENS!!! */ /* if (verbose) { ptr = eptr->ether_dhost; i = ETHER_ADDR_LEN; fprintf(stdout, " Destination MAC:\t"); do { fprintf(stdout, "%s%x",(i == ETHER_ADDR_LEN) ? " " : ":",*ptr++); } while(--i>0); fprintf(stdout, "\n"); ptr = eptr->ether_shost; i = ETHER_ADDR_LEN; fprintf(stdout, " Source MAC:\t"); do { fprintf(stdout, "%s%x",(i == ETHER_ADDR_LEN) ? " " : ":",*ptr++); } while(--i>0); fprintf(stdout, "\n"); } */ // print IPs ipp = (struct my_ip*)(packet + sizeof(struct ether_header)); // fprintf(stdout,"original packet data:\n"); /* char *temp, dst_ip[63], src_ip[63]; temp = inet_ntoa(ipp->ip_dst); strcpy(dst_ip, temp); // possible overflow temp = inet_ntoa(ipp->ip_src); strcpy(src_ip, temp); fprintf(stdout,"SRC IP: %s\n", src_ip); fprintf(stdout,"DST IP: %s\n", dst_ip); */ /* printf("\npacket data (%d) :\n", pkthdr->caplen); for (i=0;icaplen;i++) { printf("%x ", packet[i]); } printf("\n"); */ bzero(from, MAX_NAMELEN+1); bzero(to, MAX_NAMELEN+1); bzero(message, MAX_MESSAGESIZE+1); int i, j; bool packet_check = false; i = MAILSLOT_SHIFT; if ((packet[FIRST_CH] == 0x58) && (packet[SECOND_CH] == 0x58) && (packet[THIRD_CH] == 0x03)) { packet_check = true; if (verbose) fprintf(stdout, "Grabbed packet of length %d\n",pkthdr->len); fprintf(stdout, "Recieved at ..... %s",ctime((const time_t*)&pkthdr->ts.tv_sec)); if (verbose) fprintf(stdout, "mailslot name %s\n", &packet[i]); /* copy packet structures */ i = i+strlen(&packet[i]) + 2; j = strlen(&packet[i]); strncpy(from, &packet[i], MAX_NAMELEN); i = i+j+1; strncpy(to, &packet[i], MAX_NAMELEN); i = i+strlen(to)+1; strncpy(message,&packet[i],MAX_MESSAGESIZE); /* */ /* auditor code */ /* */ /* if ((a = check_in_list(from)) && (a->ip != ipp->ip_src.s_addr) { printf("violation from %s (%s), send as %s with message:\n%s\n", real_from(ipp->ip_src), inet_ntoa(ipp->ip_src), from, message); } else { */ printf("from %s (%s)\t", from, inet_ntoa(ipp->ip_src)); printf(" => %s (%s)\n", to, inet_ntoa(ipp->ip_dst)); /* } */ if (verbose) printf("%s\n", recode_string(request, message)); else /* if broadcast messages mode -b */ if (show_broadcast && (ipp->ip_dst.s_addr == brdcastp)) { printf("%s\n", recode_string(request, message)); } } else { // fprintf(stderr, "signature check failed, mailslot name %s\n", &packet[i]); printf("BROWSE from (%s)\t", inet_ntoa(ipp->ip_src)); printf(" => (%s)\n", inet_ntoa(ipp->ip_dst)); } fprintf(stdout, "\n"); count++; } fflush(stdout); } int main(int argc,char **argv) { char *dev; char *s_charset; char pcap_errbuf[PCAP_ERRBUF_SIZE]; pcap_t* descr; // const u_char *packet; struct pcap_pkthdr hdr; /* pcap.h */ struct ether_header *eptr; /* net/ethernet.h */ struct bpf_program fp; /* hold compiled program */ // char *bpf_prg; int nuid, ngid; struct passwd *pwent; char *s_lang, *s_mod; char recode_pat[30] = {"866.."}; // begining part of recode request int next_option; /* A string listing valid short options letters. */ const char* const short_options = "hi:vbr:"; /* An array describing valid long options. */ const struct option long_options[] = { { "help", 0, NULL, 'h' }, { "interface", 1, NULL, 'i' }, // { "program", 1, NULL, 'p' }, { "verbose", 0, NULL, 'v' }, { "recode", 1, NULL, 'r' }, { NULL, 0, NULL, 0 } /* Required at end of array. */ }; /* Remember the name of the program, to incorporate in messages. The name is stored in argv[0]. */ program_name = argv[0]; int dev_init = 0; // int bpf_init = 0; s_charset = NULL; if (verbose) { printf("defines as follows:\n\tfirst_ch = %d\n\tsecond_ch = %d\n\t", FIRST_CH, SECOND_CH); printf("mail_shift = %d\n", MAILSLOT_SHIFT); } do { next_option = getopt_long (argc, argv, short_options, long_options, NULL); switch (next_option) { case 'h': /* -h or --help */ /* User has requested usage information. Print it to standard output, and exit with exit code zero (normal termination). */ print_usage (stdout, 0); case 'i': /* -i or --interface */ dev = optarg; dev_init = 1; break; case 'b': show_broadcast = 1; break; case 'r': s_charset = optarg; break; /* case 'p': bpf_prg = (char *)malloc(strlen(optarg)+1); if (bpf_prg) { strcpy(bpf_prg, optarg); // possible overflow if (verbose) printf("bpf program to use '%s'\n", bpf_prg); } else { fprintf(stderr, "unable to allocate memory for bpf program. terminated.\n"); exit(ENOMEM); } bpf_init = 1; break; /* * Mon Aug 8 18:52:05 MSD 2005 тут кстати сейчас читал про poptop, в нем есть bcrelay интерсно как реализован, просто исходники некогда смотреть пока. идея вот в чем, теоретически можно сделать форвардер пакетов, на основе bpf_prg, то есть по своим заданным правилам. например есть еще http://unix.freshmeat.net/projects/udp_broadcast_fw/ или http://www.google.com/search?q=udp+broadcast+packet+forwarder но большинство из виденных мной программ биндятся (то есть занимают) на указанный порт, а сделав реализацию на pcap/libnet можно обойтись и без занятия порта. а сейчас пока у меня работает форвард броадкаст пакетов с помошью kernel/iptables (P-o-M externsions: ROUTE, u32) */ case 'v': /* -v or --verbose */ verbose = 1; break; case '?': /* The user specified an invalid option. */ /* Print usage information to standard error, and exit with exit code one (indicating abonormal termination). */ print_usage (stderr, 1); case -1: /* Done with options. */ break; default: /* Something else: unexpected. */ abort (); } } while (next_option != -1); pwent = getpwnam(nobody); nuid = pwent->pw_uid; ngid = pwent->pw_gid; /* prepare recode library */ RECODE_OUTER outer = recode_new_outer (true); request = recode_new_request (outer); // bool success; /* $ man 3 setlocale A locale name is typically of the form language[_territory][.code-set][@modifier], where language is an ISO 639 language code, territory is an ISO 3166 country code, and codeset is a character set or encoding identifier like ISO-8859-1 or UTF-8. For a list of all supported locales, try "locale -a", cf. locale(1). */ if (!s_charset) { s_lang = getenv("LANG"); // setlocale() gets me "C" everytime, so i droped it for LANG env variable if (s_lang) { if (verbose) printf("current LANG enviroment variable = %s\n", s_lang); s_charset = index(s_lang, '.'); if (s_charset) { s_charset += 1; s_mod = strchr(s_lang,'@'); /* if (s_mod) printf("modifier at position %i\n",strlen(s_lang)-strlen(s_mod)+1); else printf("no modifier found\n"); */ if (s_mod) // cut modifier off s_charset[strlen(s_charset)-strlen(s_mod)]='\0'; if (verbose) printf("after dot we have %s , resulting ", s_charset); // code cut } } } // code paste if (strlen(s_charset)<10) { // process only 10 bytes small charsets strcat(recode_pat, s_charset); // add missing part and possible buffer overflow if (verbose) printf("recode request pattern '%s'\n", recode_pat); recode_scan_request (request, recode_pat); /* not to koi8-r but current locale by setlocale() system call */ } else { if (verbose) { fprintf(stderr, "Charset can't be more than 10 characters in length, you have %i\n", strlen(s_charset)); fprintf(stderr, "Unable to prepare recode library. Message recoding will not be available.\n"); } } /* grab a device to peak into... */ if (! dev_init) dev = pcap_lookupdev(pcap_errbuf); /* ask tarkus to make *peek* *peek* now, he is expert */ if (dev) { if (verbose) printf("using %s device for packet capture\n", dev); } else { fprintf(stderr,"%s\n",pcap_errbuf); exit(1); } /* ask pcap for the network address and mask of the device */ if (pcap_lookupnet(dev,&netp,&maskp,pcap_errbuf) == -1) { fprintf(stderr,"error calling pcap_lookupnet\n"); fprintf(stderr,"error message is %s\n", pcap_errbuf); } else { brdcastp = (netp & maskp | ~maskp); if (verbose) { struct in_addr tmp1; tmp1.s_addr = netp; printf("network %s , ", inet_ntoa(tmp1)); tmp1.s_addr = maskp; printf("netmask %s , ", inet_ntoa(tmp1)); tmp1.s_addr = brdcastp; printf("broadcast %s\n", inet_ntoa(tmp1)); } } /* open device for reading this time lets set it in promiscuous * mode so we can monitor traffic to another machine */ descr = pcap_open_live(dev,BUFSIZ,0,-1,pcap_errbuf); if(descr == NULL) { fprintf(stderr, "pcap_open_live(): %s\n",pcap_errbuf); exit(1); } /* Lets try and compile the program.. non-optimized */ if(pcap_compile(descr,&fp,lim_bpf_prg,0,netp) == -1) { fprintf(stderr,"Error calling pcap_compile\n"); exit(1); } /* set the compiled program as the filter */ if(pcap_setfilter(descr,&fp) == -1) { fprintf(stderr,"Error setting filter\n"); exit(1); } // droping user privileges to nobody if(!setgid(ngid)) { if (verbose) fprintf(stdout, "Changed groupid to %s(%i).\n", nobody, ngid); } else fprintf(stderr, "Error changing groupid: %s.\n", strerror(errno)); if(!setuid(nuid)) { if (verbose) fprintf(stdout, "Changed userid to %s(%i).\n", nobody, nuid); } else fprintf(stderr, "Error changing userid: %s.\n", strerror(errno)); /* ... and loop */ if (verbose) printf("entering loop...\n\n"); pcap_loop(descr,-1,my_callback,NULL); /* hey ho! not reachable code here */ recode_delete_request (request); recode_delete_outer (outer); return 0; }