/****************************************************************************/
/* 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);
}
}