Bruteforcing d'un checksum md5

Cette source est considérée comme dangereuse, elle a néamoins été gardée dans un but pédagogique :
Cette source est considérée comme dangereuse, elle a néamoins été gardée dans un but pédagogique.

Description

Bon alor l'histoire commence ici : http://www.phpcs.com/code.aspx?ID=19322
J'ai pas testé le script, mais l'idée m'intéressait de savoir qu'elle est le puissance nécéssaire pour trouver un mot de passe valide sur mon site, et en combien de temps.
J'ai donc codé ça en C (LccWin32, m'enfin ça devrait tourner sur n'importe quel autre compilo sans trop de problèmes) en vitesse, le code est vraiment cochon (j'ai même repris 3 fonctions venant de BCX, et j'ai une tonne d'includes qui servent a rien, je sais, mais j'ai commenté comme j'ai pu pour le rendre compréhenssible), j'ai pas vraiment optimisé et ça me donne quand même dans les 10000 'mot de passes' testé par seconde, avec mon simple athlon 1.3 Ghz (sans compter toutes les taches de fond qui me pompe le CPU) !
J'ai donc essayer de 'décrypter' l'exemple de mavounet (aa36dc6e81e2ac7ad03e12fedcb6a2c0=mdp), et comme vous le voyez sur la capture il met que 6s !!! Bon d'acord après ça se complique si on passe au dessus de 3 caractères, et c'est considérablement ralenti, et ça devient quasi-impossible.
Conclusion: N'utilisez QUE des mots de passes complexes contenants un bon nombre de caractères, et le mieux seraient encore d'éviter de rendre public la liste des md5 !

Source / Exemple :


#include <windows.h>
#include <stdio.h>
#include "md5.h" //Le fichier magique qui permet d'encoder en md5

// *************************************************
//                System Variables
// *************************************************

COORD   cursor;
HANDLE  hConsole;
int     color_fg = 7;
int     color_bg = 0;

// *************************************************
//               Standard Prototypes
// *************************************************

void    cls(void);
void    color (int,int);
void    locate (int,int,int=1,int=12);

// *************************************************
//                  Main Program
// *************************************************

DWORD BrutesMade=0;
DWORD NumberFounds=0;
BOOL Tourne=TRUE;
md5_byte_t buffer[1024];
DWORD nForceChars=0;
md5_byte_t searched[16];

/* ChangeLog
-les déclarations des variables pour calculer le md5 sont déclarés AVANT la boucle et plus pendant
-j'ai changé le char par un WORD (le int était pas une mauvaise idée, mais j'ai l'impression que c'est un peu plus lent chez moi)
-suprimé la conversion du md5 format brut vers le format héxa, et je compare le digest lui même (sans oublier de convertir avant le md5 que l'on recherche en format brut), mainteant sur mon pc ça tourne a 600000/s au lieu de 10000, 60x plus rapide !!!
-supression de la récusivité qui se fesait quand même bien sentir, merci à ccarniel
-adaptation a VC++ : plus grand rapidité que avec Lcc, mais mauvaise portabilité (il se trouv que le même exe, compilé sur une machine tourne moins bien sur une autre machine plus puissante !!!)
-et amélioration de la fonction de monitoring qui est maintenant plus précise (encore merci a ccarniel)

  • /
void brute_force(DWORD max, md5_byte_t searched[16]) { char md5buf[33]; DWORD maxm1=max-1; unsigned char ChiffreDebut = ' ', ChiffreFin = 254; int Curseur; bool Termine = false; md5_state_t state; md5_byte_t digest[16]; // Combinaison = new unsigned char[max+1]; memset(buffer, ChiffreDebut, (sizeof(char))*max); while (!Termine) { BrutesMade++; //On ajoute 1 au compteur de 'pass' testés //On fait un checksum md5 sur le buffer md5_init(&state); md5_append(&state, (const md5_byte_t*)buffer, max); md5_finish(&state, digest); if (!memcmp(digest, searched, 16)) { //memcmp est bcp plus rapide que strcmp, mais ne fait pas de comparaisons entre les minusucules et les majusucules, attention a ça quand vous mettez votre md5 a 'décrypter' ! locate(++NumberFounds, 29); //Si trouvé, on place le curseur a droite dans la console for (int di = 0; di < 16; ++di) sprintf(md5buf + di * 2, "%02x", digest[di]); //Ok c'est pas le plus puissant mais on s'en fout, on le fait qu'une seule fois printf(": md5('%s')='%s'\n", buffer, md5buf); //On affiche le md5 et le pass qui correspond (c'est vrai il y a des chances de pas trouver le vrai mot de passe, mais on s'en fout, si on entre ça l'ordi va le comprendre comme le vrai mot de passe !!!) Tourne=FALSE; //On arrête le thread de monitoring ExitProcess(0); //Et on quite } Curseur = 0; if (buffer[Curseur]>ChiffreFin) { do { buffer[++Curseur]++; if (Curseur == max) return ; // On a pas trouvé la combinaison } while (buffer[Curseur]>ChiffreFin); memset(buffer, ChiffreDebut, Curseur); } } } //Un ptit thread pour savoir où en est le prog parce que sinon on se fait un peu chier lol void ThreadMonitor(void) { DWORD LastBrutes=0, Secs=0; #if defined (_MSC_VER) && (_MSC_VER >= 1020) __int64 Freq,StartTime,CurTime; #else unsigned long StartTime,CurTime; #endif // defined (_MSC_VER) && (_MSC_VER >= 1020) #if defined (_MSC_VER) && (_MSC_VER >= 1020) QueryPerformanceFrequency((LARGE_INTEGER*)&Freq); QueryPerformanceCounter((LARGE_INTEGER*)&StartTime); #else StartTime = GetTickCount(); #endif // defined (_MSC_VER) && (_MSC_VER >= 1020) do { //Secs = GetTickCount() - Start; #if defined (_MSC_VER) && (_MSC_VER >= 1020) QueryPerformanceCounter((LARGE_INTEGER*)&CurTime); Secs = (CurTime - StartTime)*1000/Freq; #else CurTime = GetTickCount(); Secs = (CurTime - StartTime); #endif // defined (_MSC_VER) && (_MSC_VER >= 1020) if( Secs >= 1000 ) { cls(); //On efface l'écran et on met le curseur en haut a gauche. Ok j'avoue la fonction vient tout droit de BCX j'ai pas eu le courage d'aller mieux chercher et g pri le premier truc qui m'est tombé sous la main printf("%d décryptés.\n",BrutesMade); printf("%d/s (%ds écoulée(s)) \n",BrutesMade/(Secs/1000),(Secs/1000)); printf("Le mot de passe a au moins %d chars.\n\n== %s ==\n\n\n", nForceChars, buffer); //On affiche les stats LastBrutes=BrutesMade; //9a c'est pour le compteur de pass par secondes } Sleep(1000); //On attenton une seconde } while (Tourne==TRUE); //Une boucle jusqu'a se que un pass correct soit trouvé } int main(int argc, char *argv[]) { hConsole = GetStdHandle(STD_OUTPUT_HANDLE); // "aa36dc6e81e2ac7ad03e12fedcb6a2c0" = mdp // "1eec414adf814acbc887f59327db58fb" = ? // "02c425157ecd32f259548b33402ff6d3" = zzzz // "95ebc3c7b3b9f1d2c40fec14415d3cb8" = zzzzz char * md5s="02c425157ecd32f259548b33402ff6d3"; char cbit[3]; cbit[2]='\0'; for (int i=0; i<32; i+=2) { memcpy(cbit, md5s+i, 2); searched[i/2]=strtol(cbit, NULL, 16); } DWORD MaxSize=16; //Le nb maximum de caractères que le pass peut faire CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)ThreadMonitor, NULL, 0, NULL); //On démarre le monitoring en tant que thread seconde ==> comme ça, ça ralenti a peine le scanning, et c'est plus simple pour les fénéants^^ for (nForceChars=1; nForceChars<=MaxSize; nForceChars++) { ZeroMemory(buffer, MaxSize+1); //On efface le buffer pour plus de sécurité brute_force(nForceChars, searched); //Et on brute force ça } return 0; } // ************************************************* // Run Time Functions // ************************************************* void locate (int row,int col,int show,int shape) { CONSOLE_CURSOR_INFO cci = {0}; cursor.X = col-1; cursor.Y = row-1; SetConsoleCursorPosition(hConsole,cursor); cci.bVisible = show; cci.dwSize = shape; SetConsoleCursorInfo(hConsole,&cci); } void cls (void) { COORD coordScreen = {0,0}; DWORD cCharsWritten; CONSOLE_SCREEN_BUFFER_INFO csbi = {0}; DWORD dwConSize; register int attr; cursor.X = 0; cursor.Y = 0; GetConsoleScreenBufferInfo( hConsole, &csbi ); dwConSize = csbi.dwSize.X * csbi.dwSize.Y; FillConsoleOutputCharacter (hConsole, 32, dwConSize,coordScreen, &cCharsWritten); attr = color_fg + color_bg * 16; FillConsoleOutputAttribute (hConsole, attr, dwConSize,coordScreen, &cCharsWritten); locate(1,1,1); } void color (int fg, int bg) { SetConsoleTextAttribute (hConsole,fg+bg*16); color_fg = fg; color_bg = bg; }

Conclusion :


A oui et pour ceux qui connaissent pas, le md5 s'est une sorte de cryptage a sens unique, on peut pas décrypter, il fait 16 octects (ben oui 16 au lieu de 32, nous on en voit la version 'humaine' qui est décodée en héxadécimal)
Un octect=256 combinaisons. 16 octets=256^16 combinaisons, soit environ 3.4 puissance 38 combinaisons possibles, ou même d'après PowerCalc exactement 340282366920938463463374607431768211456... Donc autant dire que c'est quasi impossible de tomber dessus par hasard !
Le md5 est utilisé par exemple par la plus part des logiciels peer2peer pour reconnaitre un fichier vu qu'il est statistiquement impossible que deux fichiers aient le même md5.
Et il est aussi utilisé dans les site webs, quand vous entrez votre mot de passe il est tout de suite crypté en md5, et gardé sous cette forme dans la base de données, comme ça le webmaster où même un pirate ne peux avoir votre mot de passe, mais juste sa version cryptée en md5. (Sa évite les piratages quand sur tous les même sites vous mettez le même mot de passe, compte mail inclu...)

Des commentaires seraient les bien venus si vous avez de meilleures idées que moi pour trouver un mot de passe valide.

Codes Sources

A voir également

Vous n'êtes pas encore membre ?

inscrivez-vous, c'est gratuit et ça prend moins d'une minute !

Les membres obtiennent plus de réponses que les utilisateurs anonymes.

Le fait d'être membre vous permet d'avoir un suivi détaillé de vos demandes et codes sources.

Le fait d'être membre vous permet d'avoir des options supplémentaires.