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 <openssl/md2.h>
 
        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 <openssl/evp.h>
 
        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 <stdio.h>
#include <string.h>
 
#include <openssl/evp.h>
 
#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<len;i++)
		printf("%02x",(unsigned char)(buff[i]&0xFF));	
}
 
int main(int argc, char **argv){
 
	unsigned char buff[MAX_SIZE_WORD];
	int olen;
	char *out;
 
	if(!argv[1]) {
	        printf("Usage: mymd5 word\n");
        	exit(1);
	}
 
	// copy arg string into a buffer
	if (strlen(argv[1]) < 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 <stdio.h>
#include <string.h>
 
#include <openssl/evp.h>
 
#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<len;i++)
		printf("%02x",(unsigned char)(buff[i]&0xFF));	
}
 
void strtohex(unsigned char *in, unsigned char *out,int len){
	int i;
	char tmp[3];
	tmp[2]=0;
	for(i=0;i<len/2;i++){
		tmp[0]=in[2*i];
		tmp[1]=in[2*i+1];
		out[i]=strtol(tmp,NULL,16);
	}
}
 
int main(int argc, char **argv){
 
	int tries=0;
	unsigned char buff_words[MAX_SIZE_BUFF];
	unsigned char hash[16];
	unsigned char *line=NULL;
	int len=100,read;
	int olen;
	char *out;
 
	if(argc<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("\nWords from dict 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:

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

1 response so far ↓

  • 1 papanoel // Aug 29, 2009 at 5:15 pm

    Mola :)

Leave a Comment