/* * Half open port scanner. Send SYNs, and look for a SYN-ACK. If you see one, * the port is listening. * * The whole point is to evade TCP-wrapper software and other alarm systems * which look for a fully established connection. Since this never establishes * a connection, its existance isn't logged. * * Note, this can be detected by things like TCPdump, or other raw network * monitors. * * Runs under SunOS 4.x with NIT. This is a proof-of-concept toy, not a * production model. :-) * * Mike Neuman * 12/7/93 * * You can do whatever you want with this code as long as you leave this * header intact. * * Contact information: * * Mike Neuman (mcn@EnGarde.com) - http://www.engarde.com/~mcn */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* RPC makes an rpcdump call, which may be logged by a secure portmapper. * If you're paranoid and want to guess at rpc stuff, specify -r */ main(argc,argv) int argc; char *argv[]; { struct sockaddr_in server; struct servent *sp; struct hostent *hp; int c, s, count, userpc=1, sock=RPC_ANYSOCK, minport = 1, maxport = 6001; struct pmaplist *head = NULL, *headp=NULL; struct timeval timeout; register CLIENT *client; struct rpcent *rpc; unsigned long addr; char *hostp; extern char *optarg; extern int optind; /* 1: Parse options, get host addr */ while ((c = getopt(argc, argv, "rhm:M:")) != -1) switch(c) { case 'r': userpc = 0; break; case 'm': minport = atoi(optarg); break; case 'M': maxport = atoi(optarg); break; case 'h': usage(argv[0]); exit(1); } if (optind != argc-1) { usage(argv[0]); exit(1); } if (minport > maxport || minport <= 0) { fprintf(stderr, "ERROR: minport must be > 0 and <= maxport\n"); usage(argv[0]); exit(1); } hostp = argv[optind]; if ((hp=gethostbyname(hostp))==NULL) { addr = inet_addr(hostp); if (addr == (u_long)-1) { fprintf(stderr, "Unknown host '%s'\n", hostp); usage(argv[0]); exit(1); } bcopy(&server.sin_addr, &addr, 4); } else { bzero((char *)&server, sizeof server); bcopy(hp->h_addr, (char *)&server.sin_addr, hp->h_length); server.sin_family=hp->h_addrtype; } printf("Connections established for host %s:\n",hostp); /* 2: Call rpc and get its ports */ head=NULL; if (userpc) { server.sin_port=htons(PMAPPORT); timeout.tv_sec=10; timeout.tv_usec=0; if ((client = clnttcp_create(&server, PMAPPROG, PMAPVERS, &sock, 50, 500))!=NULL) { if (clnt_call(client, PMAPPROC_DUMP, xdr_void, NULL, xdr_pmaplist, &head, timeout) != RPC_SUCCESS) head=NULL; } } /* 3: Try each port from minport - maxport */ for (count=minport;count<=maxport;count++) { printf("\r%6d",count); fflush(stdout); server.sin_port=count; if (!fakeconnect(&server, hostp)) { printf("\r %4d tcp ",count); sp=getservbyport(count,"tcp"); if (sp==NULL) { switch(count) { /* Put in known services not in /etc/services here */ case 2000: printf("(Xnews)"); break; case 6000: printf("(X)"); break; default: if (userpc) { headp=head; while (headp!=NULL) { if (headp->pml_map.pm_prot!=IPPROTO_TCP) { headp=headp->pml_next; continue; } if (count!=headp->pml_map.pm_port) { headp=headp->pml_next; continue; } rpc = getrpcbynumber(headp->pml_map.pm_prog); if (rpc) { printf("%-15s [rpc]", rpc->r_name); break; } else { headp=headp->pml_next; continue; } } if (rpc==NULL) printf(" [rpc]"); } else printf("???\n"); break; } } else printf("%-15s",sp->s_name); printf("\n"); } } printf("\n"); exit(0); } usage(prog) char *prog; { fprintf(stderr,"Usage: %s [-r] [-m minport] [-M maxport] host\n", prog); fprintf(stderr,"Options:\n"); fprintf(stderr,"-r Don't use RPC for port info\n"); fprintf(stderr,"-m port Specify port number to start at (default 1)\n"); fprintf(stderr,"-M port Specify port number to end at (default 6001)\n"); return; } int scan_return(fd, iph, tcph) int fd; struct ip *iph; struct tcphdr *tcph; { char buf[8*1024], *bp, *bufstop, *cp, abuf[2048]; int cc, done=0, retval=0; struct nit_bufhdr *hdrp; struct nit_iftime *ntp; struct nit_ifdrops *ndp; struct nit_iflen *nlp; struct ether_header *ep; u_short et; register struct ip *ip; struct tcphdr *tp; while (!done) { if ((cc=read(fd, buf, 8*1024)) >= 0) { bp=buf; bufstop = buf+cc; while (bp < bufstop) { cp = bp; /* Get past NIT buffer */ hdrp = (struct nit_bufhdr *)cp; cp += sizeof(*hdrp); /* get past NIT timer */ ntp = (struct nit_iftime *)cp; cp += sizeof(*ntp); /* get past the drops */ ndp = (struct nit_ifdrops *)cp; cp += sizeof(*ndp); /* get past packet len */ nlp = (struct nit_iflen *)cp; cp += sizeof(*nlp); /* next snapshot */ bp += hdrp->nhb_totlen; /* Okay, got the packet, make sure it's a SYN-ACK reply */ ep = (struct ether_header *)cp; et = ntohs(ep->ether_type); if ( et >= ETHERTYPE_TRAIL && et < ETHERTYPE_TRAIL+ETHERTYPE_NTRAILER) continue; cp += sizeof(struct ether_header); nlp->nh_pktlen -= sizeof(struct ether_header); if (et != ETHERTYPE_IP) continue; /* It's: ethernet + IP */ ip=(struct ip *)cp; bcopy((char *)ip, (char *)abuf, nlp->nh_pktlen); /* Align */ ip = (struct ip *)abuf; if (ip->ip_p != IPPROTO_TCP) continue; /* It's ethernet + IP + TCP */ if (ip->ip_src.s_addr != iph->ip_dst.s_addr) continue; if (ip->ip_dst.s_addr != iph->ip_src.s_addr) continue; /* It's ethernet + IP + TCP + from target + to us */ cp+=sizeof(struct ip); tp = (struct tcphdr *)cp; if (tp->th_sport != tcph->th_dport) continue; if (tp->th_dport != tcph->th_sport) continue; /* It's ethernet + IP + TCP + from target + to us + ports right */ if ((tp->th_flags & (TH_SYN|TH_ACK)) == (TH_SYN|TH_ACK)) { retval=0; done=1; } else { retval=1; done=1; } break; } /* While (bpsin_family = AF_INET; s_arp->sin_addr.s_addr = server->sin_addr.s_addr; /* First, send junk to it to get an ARP entry */ s_arp->sin_port = 9; sendto(s, "blah", 4, 0, (struct sockaddr *)s_arp, sizeof(struct sockaddr_in)); s_arp = (struct sockaddr_in *)&arpreq.arp_ha; s_arp->sin_family = AF_UNSPEC; if (ioctl(s, SIOCGARP, &arpreq) < 0) { perror("ioctl"); close(s); exit(1); } if (arpreq.arp_flags & ATF_COM) { bcopy(arpreq.arp_ha.sa_data, &faddr, 6); } else { fprintf(stderr, "Can't find destination/router hardware address.\n"); exit(1); } /* faddr and myaddr are now computed, create the ether header */ bcopy(&faddr, &eh.ether_dhost, 6); eh.ether_type = htons(ETHERTYPE_IP); sa.sa_family = AF_UNSPEC; bcopy((char *)&eh, (char *)sa.sa_data, sizeof(struct ether_header)); ctl.len = sizeof(struct sockaddr); ctl.buf = (char *)&sa; bp = buf; iph.ip_v = IPVERSION; iph.ip_hl = 5; /* 20 octets--no options */ iph.ip_tos = htons(0); /* No special service */ iph.ip_len = htons(40); iph.ip_id = htons(27); /* "unique" identification */ iph.ip_off = htons(0); iph.ip_ttl = htons(60); iph.ip_p = htons(IPPROTO_TCP); iph.ip_dst.s_addr = server->sin_addr.s_addr; gethostname(buf, 2048); he = gethostbyname(buf); bcopy((char *)he->h_addr, (char *)&iph.ip_src.s_addr, sizeof(iph.ip_src.s_addr)); iph.ip_sum = 0; iph.ip_sum = htons(in_cksum_c(&iph, (iph.ip_hl << 2))); bcopy((char *)&iph, bp, sizeof(struct ip)); bp += sizeof(struct ip); oldbp = bp; computed=1; } /* if !computed */ /* Set up the TCP header */ bp = oldbp; tcph.th_sport = 5894; /* Random number */ tcph.th_dport = server->sin_port; tcph.th_seq = 59595; /* Random number */ tcph.th_ack = 0; tcph.th_off = 5; tcph.th_flags = TH_SYN; tcph.th_win = 4096; tcph.th_sum = 0; /* Calculate the TCP checksum on the TCP pseudoheader*/ pbp=pbuf; bcopy((char *)&iph.ip_src.s_addr, pbp, 4); pbp+=4; bcopy((char *)&iph.ip_dst.s_addr, pbp, 4); pbp+=4; *pbp=0; pbp++; *pbp=(char)iph.ip_p; pbp++; *(u_short *)pbp=(u_short)sizeof(struct tcphdr); pbp+=2; /* go past the u_short */ bcopy((char *)&tcph, pbp, sizeof(struct tcphdr)); pbp+=sizeof(struct tcphdr); if ((pbp-pbuf)%2) { *pbp=(char)0; pbp++; } tcph.th_sum = htons(in_cksum_c(pbuf, (int)(pbp-pbuf))); bcopy((char *)&tcph, bp, sizeof(struct tcphdr)); bp += sizeof(struct tcphdr); datah.len = (int)(bp-buf); datah.buf = (char *)buf; if (putmsg(fd, &ctl, &datah, 0) < 0) { perror("putmsg (ctl)"); switch(errno) { case EAGAIN: fprintf(stderr,"EAGAIN\n");break; case EBADF: fprintf(stderr,"Bad FD\n");break; case EFAULT: fprintf(stderr,"Bad ctlptr or dataptr\n");break; case EINTR: fprintf(stderr,"Signal caught\n");break; case EINVAL: fprintf(stderr,"undefined flag\n");break; case ENOSTR: fprintf(stderr,"No stream\n");break; case ENXIO: fprintf(stderr,"hangup downstream\n");break; case ERANGE: fprintf(stderr,"data size problem\n");break; } exit(1); } /* Now that we've sent the syn, scan all return packets for reply */ if (!setjmp(env_buf)) { signal(SIGALRM, handle_alarm, -1); alarm(1); retval=scan_return(fd, &iph, &tcph); alarm(0); } else retval=1; return(retval); } int fakeconnect(server, servername) /* * Fake the connection, return 1 on failure, 0 on success */ struct sockaddr_in *server; char *servername; { static int if_fd = -1; char *device=0; u_long localnet; u_long netmask; int linktype; char *lookup_device(); void lookup_net(); if (if_fd == -1) { if (device==0) { device = lookup_device(); if (device == 0) perror("can't find any interfaces"); } if_fd = initdevice(device, &linktype); lookup_net(device, &localnet, &netmask); } return(send_packet(if_fd, server, servername, localnet, netmask)); } /* ====================== begin nasty NIT stuff ======================= */ /* Not all systems have IFF_LOOPBACK */ #ifdef IFF_LOOPBACK #define ISLOOPBACK(p) ((p)->ifr_flags & IFF_LOOPBACK) #else #define ISLOOPBACK(p) (strcmp((p)->ifr_name, "lo0") == 0) #endif char *lookup_device() { struct ifreq ibuf[16], *ifrp, *ifend, *mp; struct ifconf ifc; int fd; int minunit, n; char *cp; static char device[sizeof(ifrp->ifr_name)]; fd = socket(AF_INET, SOCK_DGRAM, 0); if (fd < 0) { perror("newscan: socket"); exit(1); } ifc.ifc_len = sizeof(ibuf); ifc.ifc_buf = (caddr_t)ibuf; if (ioctl(fd, SIOCGIFCONF, (char *)&ifc) < 0 || ifc.ifc_len < sizeof(struct ifreq)) { perror("newscan: SIOCGIFCONF: "); exit(1); } ifrp = ibuf; ifend = (struct ifreq *)((char *)ibuf + ifc.ifc_len); mp = 0; minunit = 666; while (ifrp < ifend) { struct ifreq ifr; /* * Need a template to preserve address info that is * used below to locate the next entry. (Otherwise, * SIOCGIFFLAGS stomps over it because the requests * are returned in a union.) */ bcopy(ifrp->ifr_name, ifr.ifr_name, sizeof(ifr.ifr_name)); if (ioctl(fd, SIOCGIFFLAGS, (char *)&ifr) < 0) { fprintf(stderr, "newscan: SIOCGIFFLAGS: "); perror(ifrp->ifr_name); exit(1); } if ((ifr.ifr_flags & IFF_UP) && !ISLOOPBACK(&ifr)) { for (cp = ifrp->ifr_name; !isdigit(*cp); ++cp) ; n = atoi(cp); if (n < minunit) { minunit = n; mp = ifrp; } } #if BSD >= 199006 n = ifrp->ifr_addr.sa_len + sizeof(ifrp->ifr_name); if (n < sizeof(*ifrp)) ++ifrp; else ifrp = (struct ifreq *)((char *)ifrp + n); #else ++ifrp; #endif } close(fd); if (mp == 0) return (0); (void)strcpy(device, mp->ifr_name); return (device); } /* * Get the netmask of an IP address. This routine is used if * SIOCGIFNETMASK doesn't work. */ static u_long ipaddrtonetmask(addr) u_long addr; { char str[80]; if (IN_CLASSA(addr)) return (IN_CLASSA_NET); if (IN_CLASSB(addr)) return (IN_CLASSB_NET); if (IN_CLASSC(addr)) return (IN_CLASSC_NET); sprintf(str,"unknown IP address class: %08X", addr); perror(str); /* NOTREACHED */ } void lookup_net(device, netp, maskp) char *device; u_long *netp; u_long *maskp; { int fd; struct ifreq ifr; struct sockaddr_in *sin = (struct sockaddr_in *)&ifr.ifr_addr; /* Use data gram socket to get IP address. */ if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { perror("newscan: socket"); exit(1); } (void)strncpy(ifr.ifr_name, device, sizeof(ifr.ifr_name)); if (ioctl(fd, SIOCGIFADDR, (char *)&ifr) < 0) { /* * This will fail if an IP address hasn't been assigned. */ *netp = 0; *maskp = 0; return; } *netp = sin->sin_addr.s_addr; if (ioctl(fd, SIOCGIFNETMASK, (char *)&ifr) < 0) *maskp = 0; else *maskp = sin->sin_addr.s_addr; if (*maskp == 0) *maskp = ipaddrtonetmask(*netp); *netp &= *maskp; (void)close(fd); } u_long snaplen = 0; int initdevice(device,linktype) char *device; int *linktype; { struct strioctl si; /* struct for ioctl() */ struct timeval timeout; /* timeout for ioctl() */ struct ifreq ifr; /* interface request struct */ u_long if_flags; /* modes for interface */ int ret; /* int chunksize = (8*1024); */ int chunksize = 0; int if_fd; char *dev = "/dev/nit"; int nonblock; struct ether_header eh; struct ip iptest; struct packetfilt pf; register u_short *fwp = pf.Pf_Filter; u_short ethoffset; u_short ipoffset; u_short masker = 255; si.ic_timout = INFTIM; /* Set up the Packet Filter */ ethoffset=((u_int)&eh.ether_type-(u_int)&eh.ether_dhost)/(sizeof(u_short)); ipoffset = 11; *fwp++ = ENF_PUSHZERO; *fwp++ = ENF_PUSHWORD + ethoffset; *fwp++ = ENF_PUSHLIT; *fwp++ = htons(ETHERTYPE_IP); *fwp++ = ENF_COR; *fwp++ = ENF_PUSHWORD + ipoffset; *fwp++ = ENF_PUSHLIT; *fwp++ = (u_short)255; *fwp++ = ENF_AND; *fwp++ = ENF_PUSHLIT; *fwp++ = (u_short)6; *fwp++ = ENF_COR; *fwp++ = ENF_PUSHLIT; *fwp++ = (u_short)1; /* open /dev/nit Read/write */ if ((if_fd = open(dev, O_RDWR)) < 0) { (void) fprintf(stderr, "newscan: open: "); perror(dev); exit(-1); } /* arrange to get discrete messages from the STREAM and use NIT_BUF */ ioctl(if_fd, I_SRDOPT, (char*)RMSGD); /* it is important to have this stuff in the stream BEFORE! the nbuf */ si.ic_cmd = NIOCSETF; si.ic_len = sizeof(struct packetfilt); si.ic_dp = (char*)&pf; pf.Pf_FilterLen = fwp - &pf.Pf_Filter[0]; if (ioctl(if_fd, I_PUSH, "pf")<0) { perror("I_PUSH pf"); fprintf(stderr,"No pf being used\n"); } else { if ((ioctl(if_fd, I_STR, (char*)&si)) < 0) { perror("newscan: NIOCSETF"); /* exit(-1); */ fprintf(stderr,"No pf being used (or one already applied)\n"); } } ioctl(if_fd, I_PUSH, "nbuf"); /* set the timeout */ timeout.tv_sec = 1; timeout.tv_usec = 0; si.ic_cmd = NIOCSTIME; si.ic_len = sizeof(timeout); si.ic_dp = (char*)&timeout; if ((ret = ioctl(if_fd, I_STR, (char*)&si)) < 0) { perror("newscan: NIOCSTIME"); exit(-1); } /* set the chunksize */ si.ic_cmd = NIOCSCHUNK; si.ic_len = sizeof(chunksize); si.ic_dp = (char*)&chunksize; if ((ret = ioctl(if_fd, I_STR, (char*)&si)) < 0) { perror("newscan: NIOCSCHUNK"); exit(-1); } /* Set up the NIT device (bind it,set snapshot length etc) */ /* bind the interface */ strncpy(ifr.ifr_name, device, sizeof(ifr.ifr_name)); ifr.ifr_name[sizeof(ifr.ifr_name) - 1] = ' '; si.ic_cmd = NIOCBIND; si.ic_len = sizeof(ifr); si.ic_dp = (char*)𝔦 if ((ret = ioctl(if_fd, I_STR, (char*)&si)) < 0) { (void) fprintf(stderr, "newscan: NIOCBIND"); perror(ifr.ifr_name); exit(1); } /* set the snapshot length */ si.ic_cmd = NIOCSSNAP; si.ic_len = sizeof(snaplen); si.ic_dp = (char*)&snaplen; if ((ret = ioctl(if_fd, I_STR, (char*)&si)) < 0) { perror("newscan: NIOCSSNAP"); exit(1); } /* set the interface flags */ si.ic_cmd = NIOCSFLAGS; if_flags = NI_TIMESTAMP | NI_LEN | NI_DROPS; if_flags |= NI_PROMISC; si.ic_len = sizeof(if_flags); si.ic_dp = (char*)&if_flags; if ((ioctl(if_fd, I_STR, (char*)&si)) < 0) { perror("newscan: NIOCSFLAGS"); exit(1); } nonblock = 1; ioctl(if_fd, FIONBIO,&nonblock); ioctl(if_fd, I_FLUSH, (char*)FLUSHR); /* * NIT supports only ethernets. */ *linktype = 1; return if_fd; }