Sample Code–Knock
The following two source files comprise the Knock codebase. To preserve readability, we richly comment the code but do not include any book-text inside the code. You can download the full source files from this book's companion Web site at http://www.wiley.com/compbooks/schiffman.
knock.h
/*
* $Id: knock.h,v 1.1.1.1 2002/03/13 21:01:12 route Exp $
*
* Building Open Source Network Security Tools
* knock.h - Port Scanning Technique example code
*
* Copyright (c) 2002 Mike D. Schiffman mike@infonexus.com>
* All rights reserved.
*
*/
#include <libnet.h>
#include <pcap.h>
#define SNAPLEN 94 /* Ethernet + IP + opt + TCP */
#define PROMISC 1
#define TIMEOUT 500
#define PORT_OPEN 0
#define PORT_CLOSED 1
#define PORT_OPEN_TIMEDOUT 2
#define PORT_CLOSED_TIMEDOUT 3
#define SOURCE_PORT 31337
struct knock_pacK
{
pcap_t *p; /* pcap descriptor */
struct pcap_pkthdr h; /* pcap packet header */
libnet_t *1; /* libnet descriptor */
libnet_ptag_t ip; /* IP header */
libnet_ptag_t tcpudp; /* TCP or UDP header */
libnet_plist_t *plist; /* libnet port list */
u_long src_ip; /* our IP address */
u_long dst_ip; /* host to scan */
u_char flags; /* control flags */
u_char to; /* packet read timeout */
#define NETWORK_TIMEOUT 2 /* 2 seconds and we're crying foul */
u_char scan_type; /* either TCP or UDP! */
#define SCAN_TCP 0 /* TCP */
#define SCAN_UDP 1 /* UDP */
u_char scan_subtype; /* TCP scan subtype */
#define SCAN_TCP_SYN 1 /* Half-open scan */
#define SCAN_TCP_FIN 2 /* Stealth FIN scan */
#define SCAN_TCP_XMAS 3 /* Stealth XMAS scan */
u_short port; /* current port we're scanning */
u_char *packet; /* everyone's favorite: packet! */
u_short ports_open; /* open ports */
char errbuf[LIBNET_ERRBUF_SIZE];
};
struct knock_pack *knock_init(char *, u_char, char *, u_char, u_char,
u_char, char *, char *);
void knock_destroy(struct knock_pack *);
void knock(struct knock_pack *);
int build_packet (struct knock_pack *);
int write_packet(struct knock_pack *);
int receive_packet(struct knock_pack *);
void cleanup(int);
int catch_sig(int, void (*)());
void usage(char *);
/* EOF */
knock.c
/*
* $Id: knock.c,v 1.1.1.1 2002/03/13 21:01:12 route Exp $
*
* Building Open Source Network Security Tools
* knock.c - Port Scanning Technique example code
*
* Copyright (c) 2002 Mike D. Schiffman <mike@infonexus.com>
* All rights reserved.
*
*/
#include "./knock.h"
int loop = 1;
int
main(int argc, char **argv)
{
int c;
u_char flags, to;
u_char scan_type, scan_subtype;
char *device;
struct knock_pack *kp;
char errbuf[LIBNET_ERRBUF_SIZE], host[512], p_list[100];
printf("Knock 1.0 [TCP / UDP port scanning tool]\n");
to = 0;
flags = 0;
device = NULL;
scan_type = SCAN_TCP;
scan_subtype = SCAN_TCP_SYN;
memset (&host, NULL, sizeof (host));
while ((c = getoptfargc, argv, "hi:T:t:u")) !=EOF)
{
switch (c)
{
case 'h':
usage(argv[0]);
exit(EXIT_SUCCESS);
break;
case 'i':
device = optarg;
break;
case 'T':
to = atoi(optarg);
break;
case 't':
scan_type = SCAN_TCP;
scan_subtype = atoi(optarg);
switch (scan_subtype)
{
case SCAN_TCP_SYN:
break;
case SCAN_TCP_FIN:
break;
case SCAN_TCP_XMAS:
break;
default:
usage(argv[0]);
exit(EXIT_FAILURE);
}
break;
case 'u':
scan_type = SCAN_UDP;
break;
default:
usage(argv[0]);
exit(EXIT_FAILURE);
}
}
c = argc - optind;
if (c != 2)
{
usage(argv[0]);
exit(EXIT_FAILURE);
}
else
{
/* target host */
strncpy(host, argv[optind] , sizeof (host) - 1);
/* port list */
strncpy(p_list, argvfoptind + 1], sizeof (p_list) - 1);
}
/*
* Initialize knock. Here we'll bring up libpcap and libnet.
*/
kp = knock_init (device, flags, host, scan_type, scan_subtype, to,
p_list, errbuf);
if (kp == NULL)
{
fprintf(stderr, "knock_init() failed: %s n", errbuf); goto done;
}
/* print out the scan type~*/
switch (scan_type)
{
case SCAN_UDP:
printf("UDP");
break;
case SCAN_TCP:
switch (scan_subtype)
{
case SCAN_TCP_SYN:
printf("TCP Half-open");
break;
case SCAN_TCP_FIN:
printf("TCP Stealth FIN");
break;
case SCAN_TCP_XMAS:
printf("TCP Stealth XMAS");
break;
}
}
printf("-based port scan n");
printf("<ctrl-c> to quit n");
knock(kp);
done:
if (kp)
{
printf("%d %s open n", kp->ports_open, kp->ports_open == 1 ?
"port" : "ports");
}
knock_destroy(kp);
/* shut down knock */
return (EXIT_SUCCESS);
}
struct knock_pack *
knock_init(char *device, u_char flags, char *host, u_char scan_type,
u_char scan_subtype, u_char to, char *p_list, char *errbuf)
{
struct knock_pack *kp;
/*
* We want to catch the interrupt signal so we can inform the user
* how many packets we captured before we exit.
*/
if (catch_sig(SIGINT, cleanup) == -1)
{
sprintf(errbuf, "can't catch SIGINT signal. n");
return (NULL);
}
kp = malloc (sizeof (struct knock_pack));
if (kp == NULL)
{
snprintf(errbuf, PCAP_ERRBUF_SIZE, strerror(errno));
return (NULL);
}
kp->flags = flags;
kp->scan_type = scan_type;
kp->scan_subtype = scan_subtype;
kp->to = to == 0 ? NETWORK_TIMEOUT : to;
/*
* If device is NULL, that means the user did not specify one and
* is leaving it up libpcap / libnet to find one. We'll use
* libpcap's lookup routine, but they're both from the same
* codebase so it doesn't matter… ;)
*/
if (device == NULL)
{
device = pcap_lookupdev(errbuf);
if (device == NULL)
{
return (NULL);
}
}
/*
* Open the packet capturing device with the following values:
*
* SNAPLEN: We won't need more than 80 bytes
* PROMISC: on
* The interface needs to be in promiscuous mode to capture all
* network traffic on the localnet.
* TIMEOUT: 500ms
* A 500 ms timeout is probably fine for most networks. For
* architectures that support it, you might want to tune this value
* depending on how much traffic you're seeing on the network.
*/
kp->p = pcap_open_live(device, SNAPLEN, PROMISC, TIMEOUT, errbuf);
if (kp- p == NULL)
{
return (NULL);
}
/*
* We need to make sure this is Ethernet. The DLT_EN10MB specifies
* standard 10MB and higher Ethernet.
*/
if (pcap_datalink(kp->p) != DLT_EN10MB)
{
sprintf(errbuf, "Knock only works with ethernet.\n");
return (NULL);
}
kp->l = libnet_init(LIBNET_RAW4, device, errbuf);
if (kp->l == NULL)
{
return (NULL);
}
kp->src_ip = libnet_get_ipaddr4(kp->l);
if (!(kp->dst_ip = libnet_name2addr4(kp->l, host, LIBNET_RESOLVE)))
{
sprintf(errbuf, "libnet_name2addr4(): %s",
libnet_geterror(kp->l));
return (NULL);
}
if (libnet_plist_chain_new(kp->I, &kp->plist, p_list) == -1)
{
sprintf(errbuf, "libnet_plist_chain_new(): %s",
libnet_geterror(kp->l));
return (NULL);
}
return (kp);
}
void
knock_destroy(struct knock_pack *kp)
{
if (kp)
{
if (kp->p)
{
pcap_close(kp->p) ;
}
if (kp->l)
{
libnet_destroy(kp->l);
}
}
}
int
catch_sig(int signo, void (*handler)())
{
struct sigaction action;
action.sa_handler = handler;
sigemptyset(&action.sa_mask);
action.sa_flags = 0;
if (sigaction(signo, fcaction, NULL) == -1)
{
return (-1);
}
else
{
return (1);
}
}
void
knock(struct knock_pack *kp)
{
u_short bport, eport;
/*
* Loop until user hits ctrl-c at the corrjriand prcir.pt or until we
* run out of ports to scan.
*/
for (; loop; )
{
/* set ports */
if (libnet_plist_chain_next_pair(kp->plist, &bport, &eport) < 1)
{
/* we're done */
loop = 0;
continue;
}
while (!(bport > eport) && bport != 0 && loop)
{
kp->port = bport++;
/* build a port scanning packet */
if (build_packet(kp) == -1)
{
fprintf(stderr, "build_packet: %s", kp->errbuf);
continue;
}
/* write it to the network */
if (write_packet(kp) == -1)
{
fprintf(stderr, "write_packet: %s", kp->errbuf);
continue;
}
fprintf(stderr, "port %d ", kp->port);
/* look for a response and report port status to user */
switch (receive_packet(kp))
{
case PORT_OPEN~
printf("open n");
kp->ports_open++;
break;
case PORT_OPEN_TIMEDOUT:
printf("open? (timeout)\n");
kp->ports_open++;
break;
case PORT_CLOSED:
printf("closed\n");
break;
case PORT_CLOSED_TIMEDOUT:
printf("closed? (timeout)\n");
break;
}
}
}
}
int
build_packet(struct knock_pack *kp)
{
u_char control = 0;
u_short protocol;
u_long packet_size;
/* determine total packet size and port scan type */
packet_size = LIBNET_IPV4_H + (kp->scan_type == SCANJTCP ?
LIBNET_TCP_H : LIBNET_UDP_H);
protocol = kp->scan_type == SCAN_TCP ? IPPROTO_TCP : IPPROTO_UDP;
switch (kp->scan_type)
{
case SCAN_TCP:
/* set the TCP scan type */
switch (kp->scan_subtype)
{
case SCAN_TCP_SYW:
control = TH_SYN;
break;
case SCAN_TCP_FIN:
control = TH_FIN;
break;
case SCAN_TCP_XMAS:
control = TH_FIN | TH_URG | TH_PUSH;
break;
}
/*
* Build a TCP header. If this is the first time we've hit
* this block of code, kp->tcpudp will be 0 and
* libnet_build_tcp() will create the state for the packet
* and we will save it to kp->tcpudp. Each subsequent time
* we hit this block of code libnet_build_tcp will update
* this packet template. This is the same for
* libnet_build_udp() and libnet_build_ip().
*/
kp->tcpudp = libnet_build_tcp(
SOURCE_PORT, /* source port */
kp->port, /* destination port */
0x00000bad, /* sequence number */
0x0000bad0, /* acknowledgement num */
control, /* control flags */
32767, /* window size */
0, /* checksum */
0, /* urgent pointer */
LIBNET_TCP_H, /* TCP packet size */
NULL, /* payload */
0, /* payload size */
kp->l, /* libnet handle */
kp->tcpudp); /* libnet id */
if (kp->tcpudp == -1)
{
sprintf(kp->errbuf, "Can't build TCP header: %s n",
libnet_geterror (kp->l));
return (-1);
}
break;
case SCAN_UDP:
kp->tcpudp = libnet_build_udp(
SOURCE_PORT, /* source port */
kp->port, /* destination port */
LIBNET_UDP_H, /* packet size */
0, /* checksum */
NULL, /* payload */
0, /* payload size */
kp->l, /* libnet handle */
kp->tcpudp); /* libnet id */
if (kp->tcpudp == -1)
{
sprintf(kp->errbuf, "Can't build UDP header: %s n",
libnet_geterror(kp->l));
return (-1);
}
break;
}
kp->ip = libnet_build_ipv4(
packet_size, /* total packet size */
0, /* type of service */
242, /* identification */
0, /* fragmentation */
64, /* time to live */
protocol, /* protocol */
0, /* checksum */
kp->src_ip, /* source */
kp->dst_ip, /* destination */
NULL, /* payload */
0, /* payload size */
kp->l, /* libnet handle */
kp->ip); /* ptag */
if (kp->ip == -1)
{
sprintf(kp->errbuf, "Can't build IP header: %s n",
libnet_geterror(kp->l));
return (-1);
}
return (1);
}
int
write_packet(struct knock_pack *kp)
{
int c;
c = libnet_write(kp->l);
if (c == -1)
{
sprintf(kp->errbuf, "libnet_write(): %s n",
libnet_geterror(kp->l));
}
return (c);
}
int
receive_packet(struct knock_pack *kp)
{
u_short ip_hl ;
time_t start;
struct libnet_ipv4_hdr *ip;
struct libnet_tcp_hdr *tcp;
struct libnet_icmpv4_hdr *icmp;
struct libnet_udp_hdr *udp;
for (start = time(NULL); (time(NULL) - start) < kp- to; )
{
kp->packet = (u_char *)pcap_next(kp->p, &kp->h);
if (kp->packet == NULL)
{
/*
* We have to be careful here as pcap_next() can return
* NULL if the timer expires with no data in the packet
* buffer or under some special circumstances under linux.
*/
continue;
}
/*
* By using libnet's natively defined protocol headers, we can
* cast our received IP packet and access all header fields
* directly. As you'll see, this is much easier than the
* bitwise stuff we had to do in the last chapter. Also you'll
* note the lack of endian concern when dealing with libnet.
* It handles all of this for us. How nice and thoughtful of
* libnet.
*/
ip = (struct libnet_ipv4_hdr *)(kp->packet + 14);
ip_hl = ip->ip_hl << 2;
switch (ip->ip_p)
{
case IPPROTO_TCP:
if (kp->scan_type != SCAN_TCP)
{
continue;
}
tcp = (struct libnet_tcp_hdr *)(kp->packet + 14 +
ip_hl);
if (ip->ip_src.s_addr == kp->dst_ip && ip->ip_dst.s_addr == kp->src_ip && ntohs(tcp->th_sport) == kp->port && ntohs(tcp->th_dport) == SOURCE_PORT)
{
if ((tcp->th_flags & TH_SYN) &&
(tcp->th_flags & TH_ACK))
{
/* we got a SYNJACK back, we know port is open */
return (PORT_OPEN);
}
if (tcp->th_flags & TH_RST)
{
/* we got an RST back, we know port is closed */
return (PORT_CLOSED);
}
}
continue;
case IPPROTO_ICMP:
if (kp->scan_type != SCAN_UDP)
{
continue;
}
icmp = (struct libnet_icmpv4_hdr *)
(kp->packet + 14 + ip_hl);
if (icmp- icmp_type != ICMP_NREACH &&
icmp->icmp_code != ICMP_UNREACH_PORT)
{
/* it's not a terminal response to our packet */
continue;
}
/* past IPv4 header, past ICMPv4 header */
ip = (struct libnet_ipv4_hdr *)(kp->packet + 14
+ ip_hl + LIBNET_ICMPV4_UNREACH_H);
/* past IPv4 header, past ICMPv4 header, past IPv4 */
udp = (struct libnet_udp_hdr *)(kp->packet + 14
+ ip_hl + LIBNET_ICMPV4_UNREACH_H +
LIBNET_IPV4_H);
if (ip->ip_src.s_addr == kp->src_ip && ip->ip_dst.s_addr
== kp->dst_ip && ntohs (udp->uh_dport) == kp-> &&
ntohs(udp->uh_sport) == SOURCE_PORT)
{
/* we got an ICMP port unreach; port is closed */
return (PORT_CLOSED);
}
default:
continue;
}
}
/*
* If we get down here, the scan has timed out, and depending on
* the scan protocol and type, the port may be open or it may be
* closed.
*/ if (kp->scan_type == SCAN_TCP)
{
switch (kp->scan_subtype)
{
case SCAN_TCP_SYN:
/* for half-open TCP scans assume the port is closed */
return (PORT_CLOSED_TIMEDOUT);
case SCAN_TCP_FIN:
case SCAN_TCP_XMAS:
/* for "stealth" TCP scans assume the port is open */
return (PORT_OPEN_TIMEDOUT);
}
}
else
{
/* for UDP scans assume the port is open */
return (PORT_OPEN_TIMEDOUT);
}
/* NOTREACHED (this silences compiler warnings) */
return (PORT_CLOSED);
}
void
cleanup(int signo)
{
loop = 0;
printf("Interrupt signal caught… n");
}
void
usage(char *name)
{
printf("usage %s [options] target_host port_list n"
"-h\t\tthis blurb you see right here\n"
"-i device\tspecify a device\n"
"-T timeout\tseconds to wait for a resonse\n"
"-t scantype\tscan TCP ports "
"(1 == TCP SYN, 2 == TCP FIN, 3 == TCP XMAS)\n"
"-u\t\tscan UDP ports\n", name);
}
/* EOF */