/****************************************************************************/ /* Creeper - a simple Syn Scanner */ /* minimalistic port scanner for educational purposes */ /* Copyright (C) 2007 ithilgore - ithilgore.ryu.L@gmail.com */ /* */ /* This program is free software: you can redistribute it and/or modify */ /* it under the terms of the GNU General Public License as published by */ /* the Free Software Foundation, either version 3 of the License, or */ /* (at your option) any later version. */ /* */ /* This program is distributed in the hope that it will be useful, */ /* but WITHOUT ANY WARRANTY; without even the implied warranty of */ /* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */ /* GNU General Public License for more details. */ /* */ /* You should have received a copy of the GNU General Public License */ /* along with this program. If not, see . */ /* */ /****************************************************************************/ /****************************************************************************/ /* Creeper */ /* version 1.2 */ /* by ithilgore */ /* ithilgore.ryu.L@gmail.com */ /* */ /* compile with: gcc creeper.c -lpcap -o creeper */ /* some compilers may also need -fpack-struct */ /* */ /* Use of this code is for educational purposes only. I am not responsible */ /* for any illegal or criminal activities performed with this tool or */ /* any modifications of it. */ /* This tool is free and open software. This means you can do anything you */ /* like with it with your own responsibility and with no warranty from me. */ /* to the source. */ /* */ /* This tool has been tested so far and works sucessfully in: */ /* ----Slackware 11 with kernel 2.2.4.33 */ /* ----Arch Linux (Core Dump) kernel 2.6.23 */ /****************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #define BUFSIZE 65535 //maximum size of any datagram(16 bits the size of identifier) #define TRUE 1 #define FALSE 0 #define default_low 1 #define default_high 1024 /* change the timeout at will * it defines how long the scanner will wait for * an answer from the scanned host * careful though - testing shows < 4 is bad * it is just another factor between speed and accuracy */ #define DEFAULT_S_TIMEOUT 5 /* default snap length (maximum bytes per packet to capture) */ #define SNAP_LEN 1518 /* ethernet headers are always exactly 14 bytes */ #define SIZE_ETHERNET 14 /* Ethernet addresses are 6 bytes */ #define ETHER_ADDR_LEN 6 /* USING TCPDUMP-like header structs */ /* Ethernet header */ struct sniff_ethernet { u_char ether_dhost[ETHER_ADDR_LEN]; /* destination host address */ u_char ether_shost[ETHER_ADDR_LEN]; /* source host address */ u_short ether_type; /* IP? ARP? RARP? etc */ }; /* IP header */ struct sniff_ip { u_char ip_vhl; /* version << 4 | header length >> 2 */ u_char ip_tos; /* type of service */ u_short ip_len; /* total length */ u_short ip_id; /* identification */ u_short ip_off; /* fragment offset field */ #define IP_RF 0x8000 /* reserved fragment flag */ #define IP_DF 0x4000 /* dont fragment flag */ #define IP_MF 0x2000 /* more fragments flag */ #define IP_OFFMASK 0x1fff /* mask for fragmenting bits */ u_char ip_ttl; /* time to live */ u_char ip_p; /* protocol */ u_short ip_sum; /* checksum */ struct in_addr ip_src,ip_dst; /* source and dest address */ }; #define IP_HL(ip) (((ip)->ip_vhl) & 0x0f) #define IP_V(ip) (((ip)->ip_vhl) >> 4) /* TCP header */ typedef u_int tcp_seq; struct sniff_tcp { u_short th_sport; /* source port */ u_short th_dport; /* destination port */ tcp_seq th_seq; /* sequence number */ tcp_seq th_ack; /* acknowledgement number */ u_char th_offx2; /* data offset, rsvd */ #define TH_OFF(th) (((th)->th_offx2 & 0xf0) >> 4) u_char th_flags; #define TH_FIN 0x01 #define TH_SYN 0x02 #define TH_RST 0x04 #define TH_PUSH 0x08 #define TH_ACK 0x10 #define TH_URG 0x20 #define TH_ECE 0x40 #define TH_CWR 0x80 #define TH_FLAGS (TH_FIN|TH_SYN|TH_RST|TH_ACK|TH_URG|TH_ECE|TH_CWR) u_short th_win; /* window */ u_short th_sum; /* checksum */ u_short th_urp; /* urgent pointer */ }; /* pseudo header used for tcp checksuming * a not so well documented fact ... in public */ struct pseudo_hdr { u_int32_t src; u_int32_t dst; u_char mbz; u_char proto; u_int16_t len; }; /* Global Variables */ int verbose_mode; int stealth_mode; /* Syn Scanning */ int s_timeout; /* timeout seconds for Syn Scanning */ long int low_port = default_low; long int high_port = default_high; char *ipArg = NULL; pcap_t *session; /* Function Prototypes */ void got_packet(u_char *args, const struct pcap_pkthdr *header, const u_char *packet); void print_usage(const char *argv); int check_Port(long *lport, long *hport, char *optar); uint16_t checksum_comp(uint16_t *addr, int len); pcap_t* EnginePreparing(char *vicIP, struct sockaddr_in **ipP, pcap_t *session); struct hostent* host_resolve(void); void Syn_Scanning(void); void Connect_Scanning(void); void sigfunc(int); /* Function Prototypes end*/ /********************************MAIN PROGRAM********************************/ int main(int argc, char *argv[]) { if ( argc == 1 ) { print_usage( argv[0] ); exit(0); } int opt; while ( (opt = getopt(argc , argv , "h:vp:S") ) != -1 ) { switch (opt) { case 'h': ipArg = optarg; break; case 'v': verbose_mode = TRUE; break; case 'p': check_Port (&low_port, &high_port, optarg); break; case 'S': stealth_mode = TRUE; break; case '?': fprintf (stderr, "option inconsistency : -%c \n" "see usage(no arguments)\n", optopt); exit(EXIT_FAILURE); } } if (ipArg == NULL){ fprintf(stderr, "No host given-see usage(no arguments)\n" ); exit(EXIT_FAILURE); } if (!stealth_mode) { Connect_Scanning(); } else { if (getuid() && geteuid()) { fprintf(stderr, "Need to be root to initiate Syn Scanning\n"); exit(EXIT_FAILURE); } Syn_Scanning(); } exit(EXIT_SUCCESS); } void print_usage(const char *argv) { fprintf(stdout, "Port Scanner by ithilgore\n" "usage: %s -h Host [OPTIONS]\n" "Host -> IP or Name\n" "OPTIONS include:\n" "-v : verbose mode\n" "-p : port range (eg. -p23 , -p0-1024)\n" "-S : stealth mode on ( syn scanning )\n" "more options to be included\n\n" , argv); } int check_Port (long *lport, long *hport, char *optar) { char *s1 = optar ; //point to the char after 'p' errno = 0; *lport = strtol(s1, (char **)NULL, 10); if (errno != 0) { perror ("Port number problem \n"); exit(0); } if (!(s1 = index(s1, '-'))) { //if no port range specified (no other '-' found) *hport = *lport; return 0; } else { *hport = strtol(++s1, NULL, 10) ; if (errno != 0) { perror("Port number problem \n"); exit(0); } if (low_port > high_port) { fprintf(stdout, "low_port is higher than high_port: swapping...\n"); *lport ^= *hport; *hport ^= *lport; *lport ^= *hport; } } } struct hostent* host_resolve(void) { struct hostent *hostname; extern char * ipArg; if (!(hostname = gethostbyname(ipArg))) { fprintf (stderr, "Host name resolution failed for %s \n" "Try using the nslookup prog to find the IP address\n", ipArg); exit(EXIT_FAILURE); } if (verbose_mode) { fprintf(stdout, "Host Resolution results:\n" "Name: %s\n" "Aliases:", hostname->h_name); char **alias = hostname->h_aliases; while(*alias) { fprintf(stdout, "%s ", *alias); alias++; } char **addrs = hostname->h_addr_list; fprintf(stdout, "\nIP address/es:\n"); while(*addrs) { fprintf(stdout, " %s ", inet_ntoa(*(struct in_addr *)*addrs)); addrs++; } printf("\n"); } return hostname; } void Connect_Scanning(void) { int sockfd; char temp_addr[7]; struct sockaddr_in sockaddr; struct servent *serv; struct hostent * hostname; hostname = (struct hostent *)host_resolve(); char **addr = hostname->h_addr_list; /* transfer of dotted-decimal IP of first IP from resolving */ strcpy (temp_addr, inet_ntoa(*(struct in_addr *)*addr)); fprintf(stdout, "Initiating Connect() Scan against %s [%ld ports] \n", temp_addr, (high_port-low_port+1)); int i = 0; for (i = low_port; i <= high_port; i++) { if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) { perror("Socket error"); exit(EXIT_FAILURE); } sockaddr.sin_family = AF_INET; sockaddr.sin_port = htons(i); inet_pton(AF_INET, temp_addr, &sockaddr.sin_addr); if (!connect(sockfd, (struct sockaddr*)& sockaddr, sizeof(sockaddr))) { serv = getservbyport(htons(i), "tcp"); fprintf(stdout, "TCP port %d open , possible service: %s\n", i, serv->s_name); } close(sockfd); } fprintf(stdout, "Connect Scanning completed\n"); } void sigfunc(int signum) { /* signal handler */ pcap_breakloop(session); } void Syn_Scanning(void) { s_timeout = DEFAULT_S_TIMEOUT; /* global var for timeout */ int sockfd; int timeout = 0; /* check if timeout with return from dispatch */ char temp_addr[16]; struct sockaddr_in sin; struct servent *serv; struct hostent *hostname; struct sockaddr_in *ipP; /* local ip storage */ hostname = (struct hostent *)host_resolve(); char **addr = hostname->h_addr_list; strncpy(temp_addr, inet_ntoa(*(struct in_addr *)*addr), 16); char datagram[4096]; // buffer for datagrams struct sniff_ip *iph = (struct sniff_ip *)datagram; /* tcp header begins right after the end of the ip header */ /* can it work in reverse ? Of course not */ struct sniff_tcp *tcph = (struct sniff_tcp *)(datagram + sizeof(struct sniff_ip)); struct sigaction act; act.sa_handler = sigfunc; sigemptyset(&act.sa_mask); act.sa_flags = 0; /* read man of libpcap -> SA_RESTART MUST BE OFF */ /* Prepare the Sniffing Engine */ session = (pcap_t *)EnginePreparing(temp_addr, &ipP, session); fprintf(stdout, "Initiating Syn Scanning against %s [%ld ports] \n", temp_addr, (high_port-low_port+1)); int i = 0; for (i = low_port; i <= high_port; i++) { if ((sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_TCP)) < 0) { perror("sock:"); exit(EXIT_FAILURE); } sin.sin_family = AF_INET; inet_pton(AF_INET, temp_addr, &sin.sin_addr); memset(datagram, 0, 4096); /* zero out the buffer */ iph->ip_vhl = 0x45; /* version=4,header_length=5 (no data) */ iph->ip_tos = 0; /* type of service -not needed */ iph->ip_len = sizeof (struct sniff_ip) + sizeof (struct sniff_tcp); /* no payload */ iph->ip_id = htonl(54321); /* simple id */ iph->ip_off = 0; /* no fragmentation */ iph->ip_ttl = 255; /* time to live - set max value */ iph->ip_p = IPPROTO_TCP; /* 6 as a value - see /etc/protocols/ */ iph->ip_src.s_addr = ipP->sin_addr.s_addr; /*local device IP */ iph->ip_dst.s_addr = sin.sin_addr.s_addr; /* dest addr */ iph->ip_sum = /* no need for ip sum actually */ checksum_comp( (unsigned short *)iph, sizeof(struct sniff_ip)); tcph->th_sport = htons(1234); /* arbitrary port */ tcph->th_dport = htons(i); /* scanned dest port */ tcph->th_seq = random(); /* the random SYN sequence */ tcph->th_ack = 0; /* no ACK needed */ tcph->th_offx2 = 0x50; /* 50h (5 offset) ( 8 0s reserverd )*/ tcph->th_flags = TH_SYN; /* initial connection request */ tcph->th_win = (65535); /* maximum allowed window size */ tcph->th_sum = 0; /* will compute later */ tcph->th_urp = 0; /* no urgent pointer */ /* pseudo header for tcp checksum */ struct pseudo_hdr *phdr = (struct pseudo_hdr *) (datagram + sizeof(struct sniff_ip) + sizeof(struct sniff_tcp)); phdr->src = iph->ip_src.s_addr; phdr->dst = iph->ip_dst.s_addr; phdr->mbz = 0; phdr->proto = IPPROTO_TCP; phdr->len = ntohs(0x14); /* in bytes the tcp segment length */ /*- WhyTF is it network byte saved by default ????*/ tcph->th_sum = htons(checksum_comp((unsigned short *)tcph, sizeof(struct pseudo_hdr)+ sizeof(struct sniff_tcp))); int one = 1; const int *val = &one; if (setsockopt(sockfd, IPPROTO_IP, IP_HDRINCL, val, sizeof (one)) < 0) fprintf(stderr, "Warning: Cannot set HDRINCL for port %d\n",i); if (sendto(sockfd, datagram, iph->ip_len, 0, (struct sockaddr *)&sin, sizeof(sin)) < 0) { fprintf(stderr, "Error sending datagram for port %d\n", i); break; } sigaction (SIGALRM, &act, 0); alarm(s_timeout); // give port as argument to callback function timeout = pcap_dispatch(session, -1, got_packet, (u_char *)i); alarm(0); /* trigger off alarm for this loop */ if (verbose_mode && timeout == -2) { fprintf(stdout, "timeout for port %d\n", i); } } fprintf(stdout, "SYN Scanning completed\n"); } uint16_t checksum_comp (uint16_t *addr, int len) { /* compute TCP header checksum */ /* with the usual algorithm a bit changed */ /* for byte ordering problem resolving */ /* see RFC 1071 for more info */ /* Compute Internet Checksum for "count" bytes * beginning at location "addr". */ register long sum = 0; int count = len; uint16_t temp; while (count > 1) { temp = htons(*addr++); // in this line:added -> htons sum += temp; count -= 2; } /* Add left-over byte, if any */ if(count > 0) sum += *(unsigned char *)addr; /* Fold 32-bit sum to 16 bits */ while (sum >> 16) sum = (sum & 0xffff) + (sum >> 16); uint16_t checksum = ~sum; return checksum; } pcap_t* EnginePreparing (char * vicIP, struct sockaddr_in **ipP, pcap_t *session) { char *dev; char errbuf[PCAP_ERRBUF_SIZE]; bpf_u_int32 devip, netmask; struct pcap_pkthdr header; struct bpf_program filter; /* compiled filter */ char filter_exp[30] = "src host "; /* we filter the traffic to the victim */ pcap_if_t *alldev; /* yes some numbers are HARDCODED as they should be */ /* guess why and then try exploiting it */ strncpy((char *)filter_exp+9, vicIP, 16); fprintf(stdout, "filter exp: %s \n ", filter_exp); if ((pcap_findalldevs(&alldev, errbuf)) == -1) { fprintf (stderr, "%s\n", errbuf); exit(EXIT_FAILURE); } struct pcap_addr *address = alldev->addresses; address = address->next; /*first address is U(F)O*/ struct sockaddr_in * ip; while (address) { if (address->addr) { ip = (struct sockaddr_in *) address->addr; fprintf (stdout, "Local IP: %s \n", inet_ntoa(ip->sin_addr)); } address = address->next; } *ipP = (struct sockaddr_in *)alldev->addresses->next->addr; /* local ip to be used in the raw datagram */ /* choose the first you find -there was some problem when choosing the last * ip since the last found might be 0.0.0.0 and thus chaos would occur */ dev = alldev->name; /*if ( (dev = pcap_lookupdev(errbuf)) == NULL) { printf ( "%s\n" , errbuf ) ; printf ( "Using default eth0 \n"); dev = "eth0" ; }*/ if (verbose_mode) { fprintf(stdout, "Using local IP: %s \n", inet_ntoa((*ipP)->sin_addr)); fprintf(stdout, "Using local Device: %s\n", dev); } if ((session = pcap_open_live (dev, BUFSIZE, 0, 0, errbuf)) == NULL) { fprintf(stderr, "Could not open device %s: error: %s \n ", dev, errbuf); exit(EXIT_FAILURE); } if (pcap_compile(session, &filter, filter_exp, 0, 0) == -1) { fprintf(stderr, "Couldn't parse filter %s: %s \n ", filter_exp, pcap_geterr(session)); exit(EXIT_FAILURE); } if (pcap_setfilter(session, &filter) == -1) { fprintf(stderr, "Couldn't install filter %s: %s\n", filter_exp, pcap_geterr(session)); exit(EXIT_FAILURE); } return session; } void got_packet(u_char *args, const struct pcap_pkthdr *header, const u_char *packet) { const struct sniff_tcp *tcp; const struct sniff_ip *ip; const struct sniff_ethernet *ether; struct servent *serv; int size_ip; int size_tcp; ether = (struct sniff_ethernet*) (packet); ip = (struct sniff_ip *) (packet + SIZE_ETHERNET); size_ip = IP_HL(ip)*4; if (size_ip < 20) { fprintf (stderr, "Invalid IP header length: %u bytes \n", size_ip); return; } if (ip->ip_p != IPPROTO_TCP) { fprintf (stderr, "Returned Packet is not TCP protocol \n"); return; } tcp = (struct sniff_tcp*)(packet + SIZE_ETHERNET + size_ip); size_tcp = TH_OFF(tcp)*4; if (size_tcp < 20) { fprintf (stderr, " * Invalid TCP header length: %u bytes\n", size_tcp); return; } /* the actual SYN scanning (heh) : we examine if the SYN flag is on at the receiving packet(port open) */ if (((tcp->th_flags & 0x02) == TH_SYN) && (tcp->th_flags & 0x10) == TH_ACK) { serv = getservbyport ( htons((int)args), "tcp" ); fprintf (stdout, "TCP port %d open , possible service: %s\n", args, serv->s_name); // RST is sent by kernel automatically } else if ((tcp->th_flags & 0x04 ) == TH_RST && verbose_mode) { //fprintf (stdout, "TCP port %d closed\n", args ); too much info on screen } else if (verbose_mode) { //fprintf (stdout, "Port %d state unknown/filtered \n", args); } }