/******************************************************************************
* T T L S C A N
*
* ttlscan is a libnet/libpcap/libdnet based program that sends a TCP SYN packet
* to each port of the host given via the command line. The answer is
* sniffed of the wire.
* I use it to detect hosts that fake services by forwarding packets to
* another host (behind a firewall).
* By reading header entries like the TTL, window size and IPID you might be
* able to see the OS running on the host behind the firewall.
*
* (c) 2002 by Hendrik Scholz <hendrik@scholz.net> http://www.raisdorf.net/
*
* $Id: ttlscan.c,v 1.7 2002/10/22 18:38:17 hscholz Exp $
*
******************************************************************************
* C O N F I G U R A T I O N
*
* most variables can be modified during run-time. If you want to change
* the defaults edit config.h
*
******************************************************************************
* C O M P I L E
*
* Use the Makefile to compile ttlscan. As of now it works on FreeBSD and
* NetBSD. It should be easy to add other systems.
*
******************************************************************************
* G P L B L U R B
*
* ttlscan 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 2 of the License, or
* (at your option) any later version.
*
* ttlscan 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 ttlscan; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*****************************************************************************/
#include "config.h"
#include "src/error.c"
#include "src/usage.c"
#include "src/setoffset.c"
#define CVS_VERSION "$Id: ttlscan.c,v 1.7 2002/10/22 18:38:17 hscholz Exp $"
int main (int argc, char **argv)
{
extern int debug; // the default debug level is defined in config.h
extern int timeout_secs;
extern int timeout_msecs;
extern int waittime;
int verbose = 0; // scan output verbosity
// getopt variables
extern char *optarg;
extern int optind;
extern int optopt;
extern int opterr;
extern int optreset;
int op;
unsigned int cli_lowport=1;
unsigned int cli_hiport=65535;
unsigned int cli_singleport=0;
// source
unsigned long source;
unsigned int source_port = 24223;
// destination
unsigned long dest;
unsigned int dest_port;
// network stuff
char device[MAXBUF];
pcap_t *pcap;
struct bpf_program bpffilter;
bpf_u_int32 localnet, netmask;
char pcaperr[PCAP_ERRBUF_SIZE];
char filter[MAXBUF];
unsigned char *outpacket;
int outpacket_size;
int sentsize;
int network; // libnet send socket
char libneterr[LIBNET_ERRBUF_SIZE];
// sniffing stuff
int pcap_fd,ret;
struct pcap_pkthdr packet_hdr; // pcap header of received packet
int packet_len;
fd_set sfd;
struct timeval timeout;
struct libnet_ip_hdr *iphdr;
struct libnet_tcp_hdr *tcphdr; // only for verbose mode
extern int offset; // IP header offset is defined in config.h
u_char *packet;
// libdnet stuff
intf_t *intf;
struct intf_entry *entry;
struct addr addr;
char dnet_buf[1024];
rand_t *random;
// misc
int i; // 'nuff said :)
/*
* parse command line parameters
*
*/
while ((op = getopt(argc, argv, "L:H:s:t:i:hdvV")) != EOF)
{
switch (op)
{
case 'h':
// user wants usage()
usage(0);
break;
case 'd':
// increase debug level
debug++;
break;
case 'v':
// verbose mode
verbose++;
break;
case 's':
// single port definition
if (atoi(optarg) > 0 && atoi(optarg) < 65536)
cli_singleport = atoi(optarg);
else
error(ERR_FATAL, "port must be between 1-65535");
break;
case 'L':
// lowport definition
if (atoi(optarg) > 0 && atoi(optarg) < 65536)
cli_lowport = atoi(optarg);
else
error(ERR_FATAL, "port must be between 1-65535");
break;
case 'H':
// hiport definition
if (atoi(optarg) > 0 && atoi(optarg) < 65536)
cli_hiport = atoi(optarg);
else
error(ERR_FATAL, "port must be between 1-65535");
break;
case 't':
// timeout settings
if (atoi(optarg) > 0)
timeout_secs=atoi(optarg)/1000;
timeout_msecs=atoi(optarg)-(timeout_secs*1000);
error(ERR_DEBUG, "setting timeout to %d milliseconds",
timeout_secs*1000+timeout_msecs);
break;
case 'i':
// pause between tests
// remember: !root users can flood the network with this
if (atoi(optarg) > 0)
waittime = atoi(optarg);
break;
case 'V':
// version info
printf("ttlscan version %s\n%s\n", VERSION, CVS_VERSION);
_exit(0);
break;
default:
// got a wrong parameter
usage(1);
}
}
/*
* Check for target hostname
*
*/
if (argc - optind != 1)
// oops, no more hostname here ? or two ? or three ?
usage(1);
/*
* check if lowport number is really lower than hiport, else we exit
*/
if (cli_hiport < cli_lowport)
error(ERR_FATAL, "high port is lower that low port!");
printf("running ttlscan %s on %s (%s scan) ...\n", VERSION, argv[argc-1],
(cli_singleport!=0)? "single port" :
((cli_lowport != 1 && cli_hiport != 65535) ? "custom" : "default"));
/*
* Check for root privileges
*
* We need these for libnet and libpcap and drop privileges when not
* longer needed
*
*/
if (geteuid() != 0)
{
error(ERR_FATAL,
"Sorry, can't do that Dave (need root privileges)!");
}
// lookup destination address
if ((dest = libnet_name_resolve(argv[argc-1], 1)) == 0xFFFFFFFF)
error(ERR_FATAL, "cannot resolve destination address");
entry=(struct intf_entry*) dnet_buf;
memset(entry,0,sizeof(*entry));
entry->intf_len=sizeof(dnet_buf);
if ((intf=intf_open()) == NULL)
printf("error\n");
// argv[argc-1] is our target host/ip
if (addr_aton(argv[argc-1],&addr) < 0)
error(ERR_FATAL, "cannot convert supplied host/ip");
error(ERR_DEBUG, "target: %s", addr_ntoa(&addr));
if (intf_get_dst(intf,entry,&addr) == -1)
error(ERR_FATAL, "intf_get_dst() failed");
error(ERR_DEBUG,"interface: %s", entry->intf_name);
// copy the device name to device
snprintf(device, sizeof(device), entry->intf_name);
intf_close(intf);
// determine the source IP we should use using libnet_get_ipaddr()
source = htonl(libnet_get_ipaddr(0, device, libneterr));
// open raw socket
network = libnet_open_raw_sock(IPPROTO_RAW);
if (network == -1)
error(ERR_FATAL, "cannot open raw socket for libnet writing");
// drop root privileges
setuid(getuid());
// if we have root priveleges but are not Mr. Charlie Root we exit
if ((getuid() != geteuid()) || ((getuid() == 0) && getuid() != 0))
error(ERR_FATAL, "cannot drop privileges");
// no we know the device we try to get the packet offset in the captured
// packet
if (setoffset(entry, pcap) < 0)
error(ERR_FATAL, "could not determine correct capture offset");
localnet=0; netmask=0;
/*
* initialize the random number generator based on libdnet
*/
random = rand_open();
/*
* the main scanning loop
* if -s was used on the command line we loop only once
* if -l or|and -h are given we use them
*/
if (cli_singleport !=0)
{
cli_lowport = cli_singleport;
cli_hiport = cli_singleport;
error(ERR_DEBUGV, "using single port option");
}
for (dest_port=cli_lowport;dest_port<=cli_hiport;dest_port++)
{
if ((pcap = pcap_open_live(device,1500,0,100,pcaperr)) == NULL)
error(ERR_FATAL, "cannot open pcap device %s: %s", device, pcaperr);
if (pcap_lookupnet(device, &localnet, &netmask, pcaperr) == -1)
error(ERR_FATAL, "pcap_lookupnet() failed: ", pcaperr);
outpacket_size = LIBNET_IP_H + LIBNET_TCP_H;
if (libnet_init_packet(outpacket_size, &outpacket) != 1)
error(ERR_FATAL, "cannot reserve memory for outgoing packet");
// build ip part
libnet_build_ip(
LIBNET_TCP_H, // tcp packet
0, // Type of service
rand_uint16(random), // IPID
0, // DF bit
255, // ttl
IPPROTO_TCP, // protocol
source, // source address
dest, // dest. address
NULL, // payload pointer
0, // payload size
outpacket); // output buffer
libnet_build_tcp(
source_port+dest_port, // source port
dest_port, // dest. port
0, // seq. number
0, // ack. number
TH_SYN, // this is a SYN packet
0, // window size
0, // urgent pointer
NULL, // payload pointer
0, // payload size
outpacket+LIBNET_IP_H); // output buffer
// generate checksum
libnet_do_checksum(outpacket, IPPROTO_TCP, LIBNET_TCP_H);
// update the pcap filter to match this port
snprintf(filter, MAXBUF-1, "tcp and src host %s and src port %d "
"and dst port %d", argv[argc-1], dest_port, source_port+dest_port);
error(ERR_DEBUGV, "pcap filter: %s", filter);
if (pcap_compile(pcap, &bpffilter, filter,0, netmask) == -1)
error(ERR_FATAL, "pcap_compile() failed:", pcaperr);
if (pcap_setfilter(pcap, &bpffilter) == -1)
error(ERR_FATAL, "pcap_setfilter() failed:", pcaperr);
// sniff
pcap_fd = pcap_fileno(pcap);
FD_ZERO(&sfd);
FD_SET(pcap_fd, &sfd);
// define timeout
timeout.tv_sec = timeout_secs;
timeout.tv_usec = timeout_msecs/1000;
// send the packet
sentsize = libnet_write_ip(network, outpacket, outpacket_size);
if (sentsize < outpacket_size)
error(ERR_FATAL, "libnet_write_ip(): Attempted to write %d Bytes, but only %d Bytes written", outpacket_size, sentsize);
else
error(ERR_DEBUGV, "injected packet (dest port: %d)", dest_port);
ret = -1;
if ((ret = select(pcap_fd+1,&sfd,NULL,NULL, &timeout)) < 0)
error(ERR_FATAL,"select() error");
else if (ret == 0)
error(ERR_WARNING, "select() timeout");
else
{
// analyse the packet
if (!(packet = (u_char *) pcap_next(pcap, &packet_hdr)))
{
// no packet
printf("select returns. %d\n", ret);
error(ERR_FATAL,"could not get pointer for received packet");
}
// we capture the packet including the ethernet header
// as you can see this works only on ethernet *d'oh*
packet += offset; // header offset
packet_len = packet_hdr.caplen - offset; // size of the caputured
// packet
error(ERR_DEBUG,"received packet (size: %d Bytes)", packet_len);
// we want ip packets that need to have at least a complete
// IP header and a TCP or ICMP header
if ((packet_len < (LIBNET_IP_H + LIBNET_TCP_H)) &&
(packet_len < (LIBNET_IP_H + LIBNET_ICMP_H)))
{
error(ERR_WARNING,"received packet doesn't contain full headers");
printf("packet len: %d\nneeded: %d or %d\n", packet_len, (LIBNET_IP_H + LIBNET_TCP_H), (LIBNET_IP_H + LIBNET_ICMP_H));
continue;
}
// get the header
iphdr = (struct libnet_ip_hdr *) packet;
if (iphdr->ip_v != 4)
{
error(ERR_WARNING, "ignoring this non-IPv4 packet");
continue;
}
// check if this packet was really for us
if ((iphdr->ip_dst.s_addr != source) ||
(iphdr->ip_src.s_addr != dest))
{
error(ERR_WARNING, "captured packet was not for us");
continue;
}
/*
* this packet seems to be the one we were searching for
* we just return the ttl or more if the user wants it
*
*/
if (verbose != 0)
{
// verbose output
packet +=LIBNET_IP_H;
tcphdr = (struct libnet_tcp_hdr*) packet;
printf("port=%d ttl=%d\n", dest_port, iphdr->ip_ttl);
printf("IPID: %u, window size: %u\n",
ntohs(iphdr->ip_id), ntohs(tcphdr->th_win));
printf("TCP flags: ");
if (tcphdr->th_flags & TH_FIN)
printf("FIN ");
if (tcphdr->th_flags & TH_SYN)
printf("SYN ");
if (tcphdr->th_flags & TH_RST)
printf("RST ");
if (tcphdr->th_flags & TH_PUSH)
printf("PUSH ");
if (tcphdr->th_flags & TH_ACK)
printf("ACK ");
if (tcphdr->th_flags & TH_URG)
printf("URG ");
printf("\n");
}
else
printf("port=%d ttl=%d\n", dest_port, iphdr->ip_ttl);
}
// we should wait here if -i was given (or the default time was set)
if (waittime > 0)
usleep(waittime*1000);
pcap_close(pcap);
} // end port loop
// close random number generator
rand_close(random);
return 0;
}
syntax highlighted by Code2HTML, v. 0.9.1