It should work…

Cuando cualquier trasto es útil

It should work… header image 2

Crackeador de hashes MD5 en C y OpenSSL

August 28th, 2009 · 1 Comment · hacking, linux, programming

Un hash es una función criptográfica de tipo resumen cuyo objetivo es identificar casi unívocamente un conjunto de datos. Pretende ser una función inyectiva, es decir, que para una salida (valor imagen) solo exista una entrada (valor origen) pero debido a que su salida está limitada en tamaño es posible que para entradas diferentes existan claves resultantes iguales. Eso sería una colisión y una función hash deberá tener una salida pequeña equilibrada con su resistencia a colisiones. MD5 tiene una salida fija de 128 bits, lo que vienen siendo 32 dígitos en hexadecimal.

Primero de todo vamos a hacer un programa que nos permita calcular el hash md5 de un string que se le pase y luego ya pasaremos al crackeador.

Sería más fácil hacer un script en bash o perl gracias a la utilidad CLI de OpenSSL, a la hora de calcular un solo hash no importa mucho la velocidad pero si luego queremos hacer un pequeño crackeador no podemos partir de algo que ya sabemos que va a ser mucho más lento. Así que lo que vamos a usar es C y la librería de OpenSSL.

Si no sabemos por donde empezar un extracto del man:

$ man md5
...
SYNOPSIS
 
#include 
 
        unsigned char *MD5(const unsigned char *d, unsigned long n,
                         unsigned char *md);
 
        int MD5_Init(MD5_CTX *c);
        int MD5_Update(MD5_CTX *c, const void *data,
                         unsigned long len);
        int MD5_Final(unsigned char *md, MD5_CTX *c);
...
DESCRIPTION
...
Applications should use the higher level functions EVP_DigestInit(3) etc. instead of calling the hash functions directly.
...


Vemos que podríamos añadir esa librería y usar las funciones MD5_Init(), MD5_Update y MD5_Final pero ya nos avisan que deberíamos usar unas funciones de más alto nivel así que: EVP_DigestInit

$ man EVP_DigestInit
...
SYNOPSIS
        #include 
 
        void EVP_MD_CTX_init(EVP_MD_CTX *ctx);
        EVP_MD_CTX *EVP_MD_CTX_create(void);
 
        int EVP_DigestInit_ex(EVP_MD_CTX *ctx, const EVP_MD *type, ENGINE *impl);
        int EVP_DigestUpdate(EVP_MD_CTX *ctx, const void *d, size_t cnt);
        int EVP_DigestFinal_ex(EVP_MD_CTX *ctx, unsigned char *md,
               unsigned int *s);

De paso, aprovecho para desaconsejar el uso de MD5 a día de hoy y como mínimo usar SHA1 o incluso dejarte de monsergas y pasar a SHA256, SHA512.

He intentado que el programa sea bastante fácil de entender y seguir, ahí va:

#include
#include 
 
#include 
 
#ifdef DEBUG
#define DBG 1
#else
#define DBG 0
#endif
 
#define MAX_SIZE_WORD 200
 
unsigned char *simple_digest(char *algth, char *buffer, unsigned int len, int *olen){
 
	EVP_MD *m;
	EVP_MD_CTX ctx;
	unsigned char *ret;
 
	OpenSSL_add_all_digests ();
	if (!(m = (EVP_MD*) EVP_get_digestbyname(algth)))
		return NULL;
 
	if (!(ret = (unsigned char *) malloc(EVP_MAX_MD_SIZE)))
		return NULL;
 
	EVP_DigestInit(&ctx, m);
	EVP_DigestUpdate(&ctx, buffer, len);
	EVP_DigestFinal(&ctx, ret, olen);
 
	return ret;
}
 
void hex_print(unsigned char *buff,int len){
	int i;
	for(i=0;i< MAX_SIZE_WORD ){
 
		strncpy(buff, argv[1], strlen(argv[1]));
 
		// calculate string's md5 hash
		out = simple_digest("md5",buff,strlen(argv[1]),&olen);
 
		// print results
		if (DBG){
			printf("Word: %s\n",argv[1]);
			printf("Hash: ");
		}
		hex_print(out,olen);
		printf("\n");
 
		return 0;
	}
 
	printf("\nNot computing hash\n");
	printf("\nI can feel your dark side... ;)\n");
 
	return -1;
}

Compilamos y probamos:

$ gcc mymd5.c -o mymd5 -l ssl
$ ./mymd5 passworddeelite
5b4f50aa173b977e4cd0850cf7c52bd0

Podemos comprobar su correcto funcionamiento comparando con webs online que ofrecen utilidades para calcular hashes MD5 [1], [2].

Y ahora a por el crackeador. No vamos a intentar buscar colisiones sino a partir de un diccionario sacar la clave. Le pasaremos como parámetros un fichero con el hash y un diccionario con una palabra por línea.

#include
#include 
 
#include 
 
#ifdef DEBUG
#define DBG 1
#else
#define DBG 0
#endif
 
#define MAX_SIZE_BUFF 200
 
unsigned char *simple_digest(char *algth, char *buffer, unsigned int len, int *olen){
 
	EVP_MD *m;
	EVP_MD_CTX ctx;
	unsigned char *ret;
 
	OpenSSL_add_all_digests ();
	if (!(m = (EVP_MD*) EVP_get_digestbyname(algth)))
		return NULL;
 
	if (!(ret = (unsigned char *) malloc(EVP_MAX_MD_SIZE)))
		return NULL;
 
	EVP_DigestInit(&ctx, m);
	EVP_DigestUpdate(&ctx, buffer, len);
	EVP_DigestFinal(&ctx, ret, olen);
 
	return ret;
}
 
void hex_print(unsigned char *buff,int len){
	int i;
	for(i=0;i<3) {
	        printf("Usage: mymd5crack /path/to/hashfile /path/to/dictfile\n");
        	exit(1);
	}
 
	FILE *hashfile;
	hashfile = fopen(argv[1],"r");
	if(!hashfile){
		perror(argv[1]);
	return -1;
	}
 
	FILE *dictfile;
	dictfile = fopen(argv[2],"r");
	if(!dictfile){
		perror(argv[2]);
	return -1;
	}
 
	read = getline(&line, &len, hashfile);
	if(strlen(line)!=33){
		fprintf(stderr,"ERROR: Invalid hash length %d\n",strlen(line));
		return -1;
	}	
 
	strtohex(line,hash,32);
 
	if(DBG){
			printf("Input hash: %s\n", line);
			printf("Input buffer from hash: ");
			hex_print(hash,16);
			printf("\n\n");
	}
 
	//Read words in a loop
	while((read = getline(&line, &len, dictfile)) != -1){
		if( strlen(line) < 200){
			tries++;	
 
			//Copy word into buffer
			strcpy(buff_words,line);
 
			if(DBG)
				printf("Input buffer from dict: %s \n",buff_words);
			// compute md5
			out = simple_digest("md5",buff_words,strlen(line)-1,&olen);
 
			if(DBG){
				printf("For password %s MD5 is: ",line);
				hex_print(out,olen);
				printf("\n\n");
			}
 
			//Compare computed md5 with hash
			if(memcmp(out,hash,16) == 0){
 
				printf("PASSWORD FOUND!! in %d tries: %s\n",tries,line);
 
				fclose(hashfile);
				fclose(dictfile);
				free(out);
				if(line)
					free(line);
 
				return 0;
			}
			free(out);
		}else{
			printf("\nA word from dict is too long... cough*, cough*...\n");
		}
	}
 
	printf("Password not found in %d tries!\n",tries);
 
	if(line)
		free(line);
 
	fclose(hashfile);
	fclose(dictfile);
 
	return 0;
}

Y comprobamos su funcionamiento metiendo en /tmp/hash el hash obtenido anteriormente:

$ gcc mymd5cracker.c -o mymd5cracker -l ssl
$ ./mymd5cracker /tmp/hash /tmp/dict
PASSWORD FOUND!! in 6 tries: passworddeelite

Premio! Podéis ir en paz.

Para descargar mymd5.c y mymd5cracker.c

Gracias a TuXeD que siempre está ahí para echar una mano.


Fuente original en http://vierito.es/wordpress

Similar Posts:

VN:F [1.8.4_1055]
Rating: 4.0/5 (1 vote cast)
VN:F [1.8.4_1055]
Rating: 0 (from 2 votes)
Crackeador de hashes MD5 en C y OpenSSL4.051

Tags: ···········

1 response so far ↓

  • 1 papanoel Windows Vista Mozilla Firefox 3.5.2 // Aug 29, 2009 at 5:15 pm

    Mola :)

    UN:F [1.8.4_1055]
    Rating: 0.0/5 (0 votes cast)
    UN:F [1.8.4_1055]
    Rating: 0 (from 0 votes)

Leave a Comment