/* ICMPTALK: This sends and receives messages inside ICMP_ECHO packets ** . This is a proof of concept and is hardly a hackers dream stealth tool since it needs setuid root to run. ** Since BSD derived kernels don't pass on ICMP_ECHO packets to application programs we have to use ICMP_ECHOREPLY which means the -v option cannot be used (you don't get a reply to a reply!). Compile with -DBSD for BSD kernels. Tested on Linux 2.2 & FreeBSD 4.4 03/03/02 */ #include #include #include #include #include #include #include #include #define IP_HDR_LEN 20 #define ICMP_HDR_LEN 8 /* Not sure how large can an icmp packet be. Can icmp packets be fragmented? Or is it limited by the MTU of the interface? Anyway , 1000 bytes seems enough to me. */ #define ICMP_PACKET_DATA_LEN 1000 #define SEND_PACKET_LEN (ICMP_HDR_LEN + ICMP_PACKET_DATA_LEN) #define RECV_PACKET_LEN (IP_HDR_LEN + SEND_PACKET_LEN) /*** Use unions cos its neater ***/ union { char packet[SEND_PACKET_LEN]; struct icmp icmp; } spkt; union { char packet[RECV_PACKET_LEN]; struct { struct ip ip; struct icmp icmp; } s; } rpkt; #define icmp_send spkt.icmp #define icmp_recv rpkt.s.icmp struct sockaddr_in host_from,host_to; int sock,hostlen; int promiscuous,verbose; u_short id,seq; /*** Forward declarations ***/ void init(char *addr); void send_mesg(); void recv_mesg(); u_short in_cksum(u_short *addr,int len); /*** Start here ***/ main(int argc, char **argv) { fd_set mask; char *host; int i; /* Go through command line options */ if (argc < 2) goto USAGE; host=NULL; promiscuous=0; verbose=0; for(i=1;i < argc;++i) { if (argv[i][0] != '-') { if (host) goto USAGE; host=argv[i]; continue; } if (strlen(argv[i]) != 2) goto USAGE; switch(argv[i][1]) { case 'p': promiscuous=1; break; case 'v': #ifdef BSD printf("The -v option cannot be used with BSD kernels.\n"); exit(1); #else verbose=1; break; #endif default: goto USAGE; } } if (!host) goto USAGE; init(host); write(1,"0>",2); /* Main loop */ while(1) { /* Select on stdin and the socket */ FD_ZERO(&mask); FD_SET(0,&mask); FD_SET(sock,&mask); if (select(FD_SETSIZE,&mask,0,0,0) < 1) { perror("select()"); continue; } if (FD_ISSET(0,&mask)) { send_mesg(); printf("%d>",seq); fflush(stdout); } if (FD_ISSET(sock,&mask)) recv_mesg(); } USAGE: #ifdef BSD printf("Usage: %s [-p] \n",argv[0]); #else printf("Usage: %s [-p] [-v] \n",argv[0]); #endif exit(1); } /*** Set everything up ***/ void init(char *addr) { struct hostent *hp; seq=0; hostlen=sizeof(struct sockaddr); /* Resolve IP address */ memset(&host_to,0,sizeof(host_to)); host_to.sin_family=AF_INET; if ((host_to.sin_addr.s_addr=inet_addr(addr))==-1) { if (!(hp=gethostbyname(addr))) { printf("Host '%s' not found.\n",addr); exit(1); } memcpy((char *)&host_to.sin_addr.s_addr,hp->h_addr,4); } /* Create socket */ if ((sock=socket(AF_INET,SOCK_RAW,IPPROTO_ICMP)) == -1) { perror("socket()"); exit(1); } /* Set up send packet ICMP header */ memset(spkt.packet,0,SEND_PACKET_LEN); #ifdef BSD icmp_send.icmp_type = ICMP_ECHOREPLY; #else icmp_send.icmp_type = ICMP_ECHO; #endif icmp_send.icmp_id = id = (u_short)(getpid() & 0xFFFF); } /*** Send a message in an echo packet ***/ void send_mesg() { int len; if ((len=read(0,icmp_send.icmp_data,ICMP_PACKET_DATA_LEN)) == -1) { perror("read()"); return; } icmp_send.icmp_seq=seq++; icmp_send.icmp_cksum=(short)0; icmp_send.icmp_cksum=in_cksum((u_short *)spkt.packet,ICMP_HDR_LEN+len); printf("Sending %d byte message.\n",len); if (sendto( sock, spkt.packet, ICMP_HDR_LEN+len, 0, (struct sockaddr *)&host_to,hostlen) == -1) perror("sendto()"); } /*** Receive a message in the packet. Filter out any unwanted crud ***/ void recv_mesg() { int len,i; if ((len=recvfrom( sock, rpkt.packet, RECV_PACKET_LEN, 0, (struct sockaddr *)&host_from,&hostlen)) == -1) { perror("recvfrom()"); return; } /* If its not from the site we're talking to ignore it unless we're in promiscuous mode */ if (!promiscuous && host_from.sin_addr.s_addr != host_to.sin_addr.s_addr) return; /* Only display echo packets that are not from us */ switch(icmp_recv.icmp_type) { #ifdef BSD case ICMP_ECHOREPLY: #else case ICMP_ECHO: #endif if (icmp_recv.icmp_id != id) { /* Get whole IP packet back so subtract IP hdr len too */ len = len - IP_HDR_LEN - ICMP_HDR_LEN; printf("\nReceived %d byte message from %s:\n", len,inet_ntoa(host_from.sin_addr)); for(i=0;i < len;++i) putchar(icmp_recv.icmp_data[i] < 32 ? '.' : icmp_recv.icmp_data[i]); printf("\n%d>",seq); fflush(stdout); } break; #ifndef BSD case ICMP_ECHOREPLY: if (verbose && icmp_recv.icmp_id == id) { printf("\nMessage %d received.\n%d>",icmp_recv.icmp_seq,seq); fflush(stdout); } #endif } } /*** Standard IP checksum algorithm ***/ u_short in_cksum(u_short *addr,int len) { int sum; u_short answer; sum=0; while (len>1) { sum+=*addr++; len-=2; } answer=0; if (len==1) { *(u_char *)(&answer) = *(u_char *)addr; sum+=answer; } sum=(sum >> 16)+(sum & 0xffff); sum+=(sum >> 16); answer=~sum; return answer; }