MD5 Double Hash crack

Recentemente mi sono accorto che alcuni servizi di hosting conservano nel database le passwords degli accounts in doppia hash md5.
Per doppia hash intendo

MD5(MD5(password)).

Un tentativo di forzare una password in questo stato facendo uso dei servizi online come Md5Crack o come Gdata,sarebbe totalmente inutile.
Il sistema che ho implementato è banale,prevede la ricerca della password a partire da una hash doppia,facendo uso di un dizionario: di ogni voce del dizionario viene calcolata la hash doppia e viene confrontata con l’input,se vi è un match il programma si arresta e fornisce la voce ottenuta.
Sicuramente una ricerca lineare non è il massimo,l’ideale è implementare un database distribuito come il celebre rainbow tables,ma per le hashes doppie attualmente non sembra esserci altro.
Facendo uso di un buon dizionario di passwords,come questo,il programmino impiega un tempo nell’ordine di 30 secondi per scorrere un tale dizionario,e parliamo di circa 40MB.
Nel codice faccio uso di una funzione molto importante per la gestione dei files da applicativi in C :
la funzione mmap,tale funzione consente di mappare un file sullo spazio di indirizzamento virtuale del processo,in pratica vi muoverete sul file come su un vettore void* ,fate il cast a char*e avrete una vera e propria stringa:potete dimenticarvi quindi delle odiose fseek,fwrite etc…
La funzione MD5,presa dalla libreria libssl, con header in

openssl/md5.h

ha 3 argomenti:stringa da criptare,dimensione in bytes della stringa da criptare,stringa su cui riporre l’hash.

La stringa su cui viene calcolato l’hash si presenta come un vettore di caratteri ascii,poichè non sembra esserci una funzione di libreria atoh,che converta da stringa ascii a stringa esadecimale,l’ho dovuta implementare.
Compilate tutto con

 gcc -O3 -lssl md5doublecrack.c -o md5doublecrack
Ecco il codice:

#include <openssl /md5.h>
#include <string .h>
#include <stdlib .h>
#include <sys /types.h>
#include </sys><sys /stat.h>
#include <fcntl .h>
#include <stdio .h>
#include <sys /mman.h>

#define SIZEOF_HMD 32
#define SIZEOF_AMD 16
#define FALSE 0
#define TRUE 1

void crack(char*,const char*);
int filelength(const char*);
void atoh(char*,const char*);
int match(const char* input,const char* token);
void check_parameters(int,char**);

int main(int argc,char* argv[])
{
	const char* double_hash_input;
	char* file_name;
	int fd;
	void* file_memory;
	void* file_dump;
	int file_length;

	check_parameters(argc,argv);

	double_hash_input=argv[1];
	file_name=argv[2];

	if(strlen(double_hash_input)!=32)
		exit(1);

	file_length=filelength(file_name);
	fd=open(argv[2],O_RDONLY, S_IRUSR);
	file_memory = mmap (0, file_length, PROT_READ, MAP_SHARED, fd, 0);
	close(fd);

	memcpy(file_dump=malloc(sizeof(char)*file_length),file_memory,file_length);
	munmap(file_memory,file_length);

	crack((char*)file_dump,double_hash_input);
	return;
}

void crack(char* file,const char* input_hash)
{
	char* token;
	int cracked=FALSE;
	char a_hashed_entry[SIZEOF_AMD];
	char a_double_hashed_entry[SIZEOF_AMD];
	char h_hashed_entry[SIZEOF_HMD];
	char h_double_hashed_entry[SIZEOF_HMD];
	int string_length;

	token=strtok(file,"\n");
	cracked=match(input_hash,token);	

	while(token != NULL && cracked == FALSE )
		{
			token=strtok(NULL,"\n");
			if(token == NULL)
				break;

			string_length=strlen(token);

			MD5(token,string_length,a_hashed_entry);
			atoh(h_hashed_entry,a_hashed_entry);
			MD5(h_hashed_entry,SIZEOF_HMD,a_double_hashed_entry);
			atoh(h_double_hashed_entry,a_double_hashed_entry);

			if(strncmp(h_double_hashed_entry,input_hash,SIZEOF_HMD)==0)
				cracked=TRUE;
		}

		if(cracked == TRUE)
			printf("Match found with entry %s\n",token);
		else
			printf("Sorry,no match found\n");

			free(file);
}

int filelength(const char* file_name)
{
	FILE * file = fopen (file_name,"rb");

	/*obtain the file size*/
	fseek (file , 0 , SEEK_END);
	long int size = ftell(file);
	rewind (file);
	fclose(file);
	return size;
}

int match(const char* input_hash,const char* token)
{
	char a_hashed_entry[SIZEOF_AMD];
	char a_double_hashed_entry[SIZEOF_AMD];
	char h_hashed_entry[SIZEOF_HMD];
	char h_double_hashed_entry[SIZEOF_HMD];
	int string_length;

	if(token == NULL)
		exit(1);

	string_length=strlen(token);
	MD5(token,string_length,a_hashed_entry);
	atoh(h_hashed_entry,a_hashed_entry);
	MD5(h_hashed_entry,SIZEOF_HMD,a_double_hashed_entry);
	atoh(h_double_hashed_entry,a_double_hashed_entry);
	return strncmp(h_double_hashed_entry,input_hash,SIZEOF_HMD)==0 ? TRUE : FALSE;
}

void atoh(char* string,const char* bytes)
{
	register int i;
	for(i=0;i< SIZEOF_AMD ;i++)
	{
	        sprintf(string++,"%x",(bytes[i]&0xff)>>4);
                sprintf(string++,"%x",bytes[i]&0xf);

	}
}

void check_parameters(int argc,char* argv[])
{
	if(argc-1 != 2 || strlen(argv[1])!=SIZEOF_HMD)
	{
	printf("Usage : %s [double hash] [dictionary]\n",argv[0]);
	exit(1);
	}
}

6 Risposte a “MD5 Double Hash crack”

  1. Darshan Dice:

    siccome io sono lagnuso ho fatto la stessa cosa in ruby…
    Non è ottimizzato…anzi ci sono righe che servono solo per il debug

    #!/usr/bin/ruby
    require ‘digest/md5′
    aFile = File.new(ARGV[1], “r”)
    myhash=ARGV[0]
    a=0
    puts “Scassando §#{myhash}§ con #{ARGV[1]}”
    aFile.each_line {|line| line.chomp!
    testhash=Digest::MD5.hexdigest(Digest::MD5.hexdigest(line))
    a=a+1
    if a.modulo(1024)==0 then
    puts “#{a/1024}k: #{line} #{testhash} #{myhash}”
    end
    if testhash==myhash
    oFile=File.new(”pass.dat”, “w+”)
    oFile.puts “#{line} #{testhash}”
    oFile.close
    Kernel.exit()
    end
    }
    aFile.close

  2. Ordeal Dice:

    Ancora co sto ruby ????
    BASTAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA

  3. Darshan Dice:

    La versione che ho usato per testare il mio script…che attualmente batte il tuo in prestazioni :) è questa:

    #!/usr/bin/ruby
    require ‘digest/md5′
    aFile = File.new(ARGV[1], “r”)
    myhash=ARGV[0]
    a=0
    puts “Scassando §#{myhash}§ con #{ARGV[1]}”
    aFile.each_line {|line| line.chomp!
    testhash=Digest::MD5.hexdigest(Digest::MD5.hexdigest(line))
    if testhash==myhash
    puts “#{line} #{testhash}”
    Kernel.exit()
    end
    }
    aFile.close

    real 0m40.034s
    user 0m38.990s
    sys 0m0.668s

    contro

    real 1m4.663s
    user 1m4.080s
    sys 0m0.112s

  4. Ordeal Dice:

    Ma ne sei proprio sicuro ?
    ordeal@debian:~/hackz$ time ruby1.9 ./md5doublecrack.rb 25869800ff2ef3b940b9358d66ff59f7 all.lst
    Scassando 25869800ff2ef3b940b9358d66ff59f7 con all.lst
    prova 25869800ff2ef3b940b9358d66ff59f7

    real 0m24.703s
    user 0m24.588s
    sys 0m0.052s
    ordeal@debian:~/hackz$ time ./md5doublecrack 25869800ff2ef3b940b9358d66ff59f7 all.lst
    Match found with entry prova

    real 0m20.898s
    user 0m20.688s
    sys 0m0.176s

    Per non parlare di un not found:
    ordeal@debian:~/hackz$ time ruby1.9 ./md5doublecrack.rb 25869800ff2ef3b940b9358d66ff59f8 all.lst
    Scassando 25869800ff2ef3b940b9358d66ff59f8 con all.lst

    real 2m45.976s
    user 2m36.992s
    sys 0m0.304s
    ordeal@debian:~/hackz$ time ./md5doublecrack 25869800ff2ef3b940b9358d66ff59f8 all.lst
    Sorry,no match found

    real 2m18.092s
    user 2m11.796s
    sys 0m0.252s

  5. fripp Dice:

    Ho scritto uno scriptino in Python per provare.
    Ecco il codice:
    #!/usr/bin/python

    from __future__ import with_statement
    import hashlib
    import sys

    doubleHash = sys.argv[1]

    print “Provo a scassare ” + doubleHash + “\n”

    with open(sys.argv[2],”r”) as dict:
    for line in dict:

    hashTest = hashlib.md5(hashlib.md5(line.rstrip()).hexdigest()).hexdigest()

    if hashTest == doubleHash:
    print line.rstrip() + ” ” + doubleHash

    sys.exit(0)
    Ecco i risultati:
    valinor:Desktop feanor$ time ./crack.py b415a0b7e721a8c1ebac6bc01604e16c ./dic-0294.txt
    Provo a scassare b415a0b7e721a8c1ebac6bc01604e16c

    Karntnerstrasse-Rotenturmstrasse b415a0b7e721a8c1ebac6bc01604e16c

    real 0m5.594s
    user 0m5.355s
    sys 0m0.078s

    Ecco i risultato con la stessa hash doppia e lo scriptino in Ruby :D :
    valinor:Desktop feanor$ time ./cruby.rb b415a0b7e721a8c1ebac6bc01604e16c ./dic-0294.txt
    Scassando §b415a0b7e721a8c1ebac6bc01604e16c§ con ./dic-0294.txt
    Karntnerstrasse-Rotenturmstrasse b415a0b7e721a8c1ebac6bc01604e16c

    real 0m6.255s
    user 0m5.861s
    sys 0m0.097s

    Vince Python!

  6. beppe Dice:

    ciao Rino….ma quantu ni sai?!??!

Lascia una Risposta