Benvenuto su Inforge
Rimuovi la pubblicità e partecipa alla più grande comunità italiana sul mondo digitale presente sul web sin dal 2007.
Iscriviti

Discussione Ufficiale Programmiamo con Inforge | Bruteforce in C

Una Discussione Ufficiale punta a raccogliere tutte le informazioni su un argomento o un fatto di attualità, con costanti aggiornamenti da parte del creatore e dei partecipanti.

St3ve

Utente Platinum
12 Ottobre 2011
2,059
1,332
665
Suggerimento
Ci sono molte cose diverse da fare, ma non c'è molto da dire: se siete arrivati alla feature 3 riuscirete a fare anche la 4. Quando aggiungete una (o più) nuova funzione di hash, è meglio se mantenete uno scheletro simile. Io avevo scritto
C:
void md5(char *result, const char *data, int len);
quindi il mio scheletro sarà qualcosa del tipo
C:
void nome_funzione(char *result, const char *data, int len);
Nei linguaggi di programmazione orientati agli oggetti diremmo che vogliamo un interfaccia comune per tutte le funzioni di hash. Il modo più semplice per evitare l'if-else è utilizzare un enum e uno switch-case, ma a dire il vero ci sono tante cose che possiamo fare. Ancora di più se abbiamo usato un linguaggio diverso dal C, dove magari abbiamo first-class functions e altre astrazioni che ci semplificano la vita.

Sappiamo che il digest inserito dall'utente sarà qualcosa di questo tipo
55639ca7dc70276e94f70dbc5e89adde2f215d30606d632e72b022eae51e15d0. Ogni coppia di valori rappresenta un byte espresso in esadecimale. Noi vogliamo convertire quella stringa in un array di byte grande la metà. I valori, invece di andare da 0 a F (esadecimale), andranno da 0 a 255.

Per quanto riguarda l'utility di supporto (inserisci un digest e io ti dico il nome della funzione) vi basta controllare la lunghezza della stringa: se la stringa inserita dall'utente è lunga N caratteri esadecimali, la funzione di hash dovrà fare un mapping verso stringhe di bytes lunghe N/2. Vogliamo spiegare all'utente cosa deve inserire nel vostro programma principale, quindi inutile stampare MD5 (in maiuscolo) se il vostro programma principale vuole che il nome della funzione sia md5 (in minuscolo); inutile stampare md4 se il vostro programma non lo supporta.

Domani posto la soluzione della feature 4, che sarà anche la soluzione completa di tutto il progetto.
 

St3ve

Utente Platinum
12 Ottobre 2011
2,059
1,332
665
Ultima modifica:
Soluzione completa del progetto
Finita la feature 4, ci ritroviamo con un programma in grado di:
  • calcolare diverse (nella mia soluzione, oltre 50) funzioni di hash;
  • effettuare un attacco bruteforce in base ad un charset scelto dall'utente; e
  • dato un digest, stampare la funzione (o le funzioni) di hash che potrebbero averlo generato.
Vi ricordo, un attacco bruteforce effettua una ricerca esaustiva: per trovare la password associata ad un dato digest prova tutte le possibili combinazioni di lettere e simboli (di lunghezza sempre crescente) finché non trova la password corrispondente. Un attacco bruteforce non è un attacco al dizionario: non c'è alcun file contenente le keyword da provare, ogni stringa viene generata automaticamente dal programma; il programma non può fallire, prima o poi troverà la password che stiamo cercando. L'unico problema, è che l'attacco è molto lento perché tutte le stringhe lunghe da 1 ad n caratteri generate da un charset di lunghezza k sono
$$\[\sum_{i=1}^k \frac{n (n^k - 1)}{n - 1} \approx n^{k+1}$$\]Se questo valore vi dovesse risultare poco chiaro diciamo che, in prospettiva, se volessimo fare la stessa cosa con un attacco al dizionario, preso un charset composto da minuscole, maiuscole e numeri e fermandoci a calcolare tutte le possibili combinazioni di lunghezza 8, il nostro file peserebbe quasi 200 terabytes. Con il nostro attacco bruteforce non abbiamo bisogno di occupare tanto spazio in memoria: non c'è nessun file contenente le password da testare (le generiamo sul momento) e la memoria ram consumata dal nostro programma non va oltre qualche kilobytes. L'unico problema è che per testare così tante (potenzialmente infinite) password, ci vuole molto tempo. Il codice sorgente da me postato è tendenzialmente portable; fa uso della libreria OpenSSL che è presente in Windows, Linux, MacOS e BSD.

Codice sorgente



Modo d'uso:
Bash:
# trova la password composta da solo lettere minuscole il cui md5 è 9173039d5e505f44dfa151663ce5ee52
$ ./bruteforce md5 9173039d5e505f44dfa151663ce5ee52 abcdefghijklmnopqrstuvwxyz

# trova le funzioni di hash che potrebbero aver generato il digest 28a6d190bd2446633799bf5a2e58d57084c9b538ec498ff69ac0025586b5148a
$ ./whathash 28a6d190bd2446633799bf5a2e58d57084c9b538ec498ff69ac0025586b5148a

Non escludo che ci sia qualche bug o qualche altro difetto. In tal caso, siete libero di segnalarlo e possiamo discutere su come risolvere il problema.

Conclusione
In totale, la mia soluzione consiste in circa 120 righe contando eventuali spazi. Niente di particolarmente time-demanding programmarla da zero, ma il progetto è stato comunque diviso in step per renderlo comprensibile e fattibile (con l'aiuto dello staff) anche dai principianti. Di fatto, per farmi una vaga idea di come strutturare questa idea del progetto di gruppo, mi ero preparato la soluzione per ogni feature prima dell'inizio del progetto e non mi ha richiesto molto tempo. Idealmente, al termine della quarta feature avremmo voluto continuare ancora un po' il progetto inserendo features richieste dai membri del forum.Tuttavia, il progetto non ha riscosso grande successo quindi, a meno di un'improbabile impennata di interesse, non mi sembra il caso di dilungarlo oltre. Ci abbiamo provato.

A voi lettori non partecipanti, e a voi partecipanti che non avete proseguito oltre la prima feature, vi chiedo: Cosa è andato storto? Cosa avreste voluto vedere di diverso? Vi è sembrato troppo facile o troppo complesso? Troppo lungo o troppo corto? Preferireste utilizzare altri linguaggi? Non vi è piaciuta l'idea delle scadenze a tempo fisso? Preferireste più competizione e meno didattica? Aspettiamo i vostri feedbacks!