Domanda Risolto Crack BCrypt+SHA256

misterorange

Utente Iron
3 Aprile 2022
6
1
0
10
Ultima modifica:
Salve, vorrei chiedere secondo voi qual' è il metodo più "veloce" per ottenere la password in formato raw partendo da un hash ottenuto con l' algoritmo bcrypt_sha256 con l' utilizzo di un salt, avendo in mano sia la password hashed che il salt utilizzato. Il metodo più ovvio sarebbe quello di partire da un dizionario di possibili password, ottenere un hash per ciascuna e paragonarlo con quello da decriptare, però il problema è che questo metodo è estremamente lento dato che l' algoritmo stesso lo è. Conosco gli attacchi che fanno uso di una rainbow table, ma non riesco a trovarne una (o un software che ne utilizzi una) per l' algoritmo bcrypt_sha256, in più suppongo che dal momento che viene utilizzato un salt anche l utilizzo di una rainbow table sarebbe abbastanza inefficiente. Voi come procedereste?
Python:
import binascii
import bcrypt
import hashlib

def encode(password, salt):
    """
    Questa è la funzione utilizzata per ottenere un hash dalla plain password
    """
    password = binascii.hexlify(hashlib.sha256(password.encode()).digest())
    data = bcrypt.hashpw(password, salt)
    return "%s$%s" % ('bcrypt_sha256', data.decode("ascii"))
 
Salve, vorrei chiedere secondo voi qual' è il metodo più "veloce" per ottenere la password in formato raw partendo da un hash ottenuto con l' algoritmo bcrypt_sha256 con l' utilizzo di un salt, avendo in mano sia la password hashed che il salt utilizzato. Il metodo più ovvio sarebbe quello di partire da un dizionario di possibili password, ottenere un hash per ciascuna e paragonarlo con quello da decriptare, però il problema è che questo metodo è estremamente lento dato che l' algoritmo stesso lo è. Conosco gli attacchi che fanno uso di una rainbow table, ma non riesco a trovarne una (o un software che ne utilizzi una) per l' algoritmo bcrypt_sha256, in più suppongo che dal momento che viene utilizzato un salt anche l utilizzo di una rainbow table sarebbe abbastanza inefficiente. Voi come procedereste?
Python:
import binascii
import bcrypt
import hashlib

def encode(password, salt):
    """
    Questa è la funzione utilizzata per ottenere un hash dalla plain password
    """
    password = binascii.hexlify(hashlib.sha256(password.encode()).digest())
    data = bcrypt.hashpw(password, salt)
    return "%s$%s" % ('bcrypt_sha256', data.decode("ascii"))
Credo che la cosa migliore, per quanto lenta, sia la prima opzione che hai menzionato, per cui partire da una wordlist e fare il brute-force dell'hash. Se un algoritmo è lento c'è poco da fare. Giusto per capire lo scenario, da dove arriva questo hash? E' una challenge/CTF o una necessità derivante da qualcosa real-life?
 
  • Mi piace
Reazioni: misterorange
Credo che la cosa migliore, per quanto lenta, sia la prima opzione che hai menzionato, per cui partire da una wordlist e fare il brute-force dell'hash. Se un algoritmo è lento c'è poco da fare. Giusto per capire lo scenario, da dove arriva questo hash? E' una challenge/CTF o una necessità derivante da qualcosa real-life?
Grazie, purtroppo come pensavo. Per quanto riguarda il contesto stavo riflettendo a livello teorico sulla possibilità di ottenere alcune password specifiche (magari una manciata) in formato plain partendo da un database generico contenente hash costruiti con il suddetto algoritmo. Forse la cosa migliore sarebbe far girare uno script su un istanza AWS o Google Cloud, dato che dal momento che le password nella maggior parte dei siti oramai richiedono abbastanza criteri di sicurezza per essere accettate anche per decriptare una singola password ci vorrebbe una buona quantità di tempo.

Python:
# Questo è il codice per verificare ciascuna password, la funzione verify_plain dovrebbe poi essere chiamata in loop. Tutto lento a causa delle chiamate di hashlib.sha256 e bcrypt.hashpw
import secrets
import hashlib
import binascii
import bcrypt

def force_bytes(s):
   if isinstance(s, bytes):
      return s
   return str(s).encode('utf-8', 'strict')
 
def encode(password, salt):
   password = binascii.hexlify(hashlib.sha256(password.encode()).digest())
   data = bcrypt.hashpw(password, salt)
   return "%s$%s" % ('bcrypt_sha256', data.decode("ascii"))

def verify_plain(plain_password, encoded):
   algorithm, data = encoded.split("$", 1)
   encoded_data = data.encode("ascii")
   encoded_2 = encode(plain_password, encoded_data)
   return secrets.compare_digest(force_bytes(encoded), force_bytes(encoded_2))
 
Conosco gli attacchi che fanno uso di una rainbow table, ma non riesco a trovarne una (o un software che ne utilizzi una) per l' algoritmo bcrypt_sha256, in più suppongo che dal momento che viene utilizzato un salt anche l utilizzo di una rainbow table sarebbe abbastanza inefficiente.
Non è inefficiente, è proprio inutile. Il salt si usa proprio per impedire gli attacchi con le rainbow tables.

Per quanto riguarda il contesto stavo riflettendo a livello teorico sulla possibilità di ottenere alcune password specifiche (magari una manciata) in formato plain partendo da un database generico contenente hash costruiti con il suddetto algoritmo.
Se il contesto è puramente teorico allora non c'è niente che tu possa fare. Bcrypt è un attualmente ritenuto molto sicuro, combinarlo con sha256 forse gli abbassa (da un punto di vista teorico) un po' i bit di sicurezza, ma è comunque sicuro. Quello che puoi fare è attaccarlo in un contesto pratico: spesso gli utenti usano password deboli, usano la stessa password anche per altre cose, magari il tizio che ha scritto il sito/servizio ha usato l'algoritmo in modo sbagliato, forse c'è un bug nella libreria, etc...

Voi come procedereste?
Tanto per cominciare puoi scrivere l'algoritmo in C e farlo girare sulla GPU (Cuda / OpenCL), oppure puoi usare un programma già ottimizzato per fare tutte queste cose. Python è un linguaggio notoriamente molto lento. È vero che in questo caso bottleneck è tutto nella libreria, ma in casi così estremi io eviterei del tutto Python. Lancia un attacco al dizionario (i.e., prova a tante password comuni) e cerca informazioni sulla vittima e da dove proviene quel digest: se la vittima ha scelto una password "a regola d'arte" e se il sito/servizio da cui proviene quel digest non ha commesso errori, tu non puoi farci proprio niente.
Affidati all'errore umano, non perdere tempo a cercare fragilità nell'algoritmo.
 
Non è inefficiente, è proprio inutile. Il salt si usa proprio per impedire gli attacchi con le rainbow tables.


Se il contesto è puramente teorico allora non c'è niente che tu possa fare. Bcrypt è un attualmente ritenuto molto sicuro, combinarlo con sha256 forse gli abbassa (da un punto di vista teorico) un po' i bit di sicurezza, ma è comunque sicuro. Quello che puoi fare è attaccarlo in un contesto pratico: spesso gli utenti usano password deboli, usano la stessa password anche per altre cose, magari il tizio che ha scritto il sito/servizio ha usato l'algoritmo in modo sbagliato, forse c'è un bug nella libreria, etc...


Tanto per cominciare puoi scrivere l'algoritmo in C e farlo girare sulla GPU (Cuda / OpenCL), oppure puoi usare un programma già ottimizzato per fare tutte queste cose. Python è un linguaggio notoriamente molto lento. È vero che in questo caso bottleneck è tutto nella libreria, ma in casi così estremi io eviterei del tutto Python. Lancia un attacco al dizionario (i.e., prova a tante password comuni) e cerca informazioni sulla vittima e da dove proviene quel digest: se la vittima ha scelto una password "a regola d'arte" e se il sito/servizio da cui proviene quel digest non ha commesso errori, tu non puoi farci proprio niente.
Affidati all'errore umano, non perdere tempo a cercare fragilità nell'algoritmo.
Utilizzare la GPU per sfruttare la potenza di calcolo in parallelo? Oppure per qualche altro motivo?
 
Non è inefficiente, è proprio inutile. Il salt si usa proprio per impedire gli attacchi con le rainbow tables.


Se il contesto è puramente teorico allora non c'è niente che tu possa fare. Bcrypt è un attualmente ritenuto molto sicuro, combinarlo con sha256 forse gli abbassa (da un punto di vista teorico) un po' i bit di sicurezza, ma è comunque sicuro. Quello che puoi fare è attaccarlo in un contesto pratico: spesso gli utenti usano password deboli, usano la stessa password anche per altre cose, magari il tizio che ha scritto il sito/servizio ha usato l'algoritmo in modo sbagliato, forse c'è un bug nella libreria, etc...


Tanto per cominciare puoi scrivere l'algoritmo in C e farlo girare sulla GPU (Cuda / OpenCL), oppure puoi usare un programma già ottimizzato per fare tutte queste cose. Python è un linguaggio notoriamente molto lento. È vero che in questo caso bottleneck è tutto nella libreria, ma in casi così estremi io eviterei del tutto Python. Lancia un attacco al dizionario (i.e., prova a tante password comuni) e cerca informazioni sulla vittima e da dove proviene quel digest: se la vittima ha scelto una password "a regola d'arte" e se il sito/servizio da cui proviene quel digest non ha commesso errori, tu non puoi farci proprio niente.
Affidati all'errore umano, non perdere tempo a cercare fragilità nell'algoritmo.
Grazie mille per la risposta. Penso anche io che la soluzione sia riscrivere il codice in un linguaggio di basso livello ed in seguito sfruttare la GPU. Per quanto riguarda sfruttare errori commessi dietro le quinte la cosa risulta assai difficile dato che mi interessano prevalentemente casi in cui vengono utilizzati web framework la cui gestione delle password arriva out of the box e che impostano un requisito minimo per le password eliminando quelle più deboli. Riguardo al consiglio di utilizzare un programma ottimizzato esistente, ne conosci uno che funzioni con bcrypt_sha256 specificamente (mi sembra che se ne trovino per bcrypt e sha256 ma non mischiati insieme)?
 
Grazie mille per la risposta. Penso anche io che la soluzione sia riscrivere il codice in un linguaggio di basso livello ed in seguito sfruttare la GPU. Per quanto riguarda sfruttare errori commessi dietro le quinte la cosa risulta assai difficile dato che mi interessano prevalentemente casi in cui vengono utilizzati web framework la cui gestione delle password arriva out of the box e che impostano un requisito minimo per le password eliminando quelle più deboli. Riguardo al consiglio di utilizzare un programma ottimizzato esistente, ne conosci uno che funzioni con bcrypt_sha256 specificamente (mi sembra che se ne trovino per bcrypt e sha256 ma non mischiati insieme)?
Ieri avevo guardato sia per Hashcat che John e non mi pare supportino tale combo di algoritmi. Non so se esistano altri tool che lo facciano, se sì non ne sono a conoscenza, ma vista la specificità penso che la cosa migliore sia fare come ha detto st3ve
 
Utilizzare la GPU per sfruttare la potenza di calcolo in parallelo? Oppure per qualche altro motivo?
Sì, per il calcolo parallelo. Gli algoritmi moderni cercano di ostacolare questo trend perché con le cryptocurrencies in auge non è così difficile procurarsi hardware "spacca hash". Per esempio, scrypt funziona tipo bcrypt (i.e., hai un parametro per renderlo più veloce o più lento) solo che oltre a configurare la lentezza dell'algoritmo puoi configurare anche la pesantezza: non puoi testare tante password contemporaneamente perché non ti basta la (V)RAM.

Riguardo al consiglio di utilizzare un programma ottimizzato esistente, ne conosci uno che funzioni con bcrypt_sha256 specificamente (mi sembra che se ne trovino per bcrypt e sha256 ma non mischiati insieme)?
Quel bcrypt_sha256 non è altro che sha256 applicato prima di bcrypt. Fai i dovuti test e verifica tu stesso. Il motivo per cui si usa è dovuto al fatto che bcrypt ha un limite sulla dimensione del plaintext. Ci rimetti un po' in sicurezza perché le fragilità di sha256 adesso si riflettono parzialmente anche su bcrypt, ma ci guadagni qualcosina in flessibilità. Il fatto che questo web framework di cui parli scelga di fare questa roba piuttosto che dire agli user di non usare password ridicolmente lunghe (oltre 72 caratteri) dovrebbe farti capire che gli errori dietro le quinte non sono poi così fuori discussione. Immagino che i programmi citati da 0xbro non supportino nativamente bcrypt_sha256 proprio perché non è una tecnica particolarmente sensata dal punto di vista della sicurezza. Detto questo, non dovrebbe essere particolarmente difficile combinare due programmi, le utilities di unix si basano molto sul concetto "l'output di uno dev'essere l'input dell'altro", altrimenti cerca qualcosa di prefabbricato in rete. Non è roba che ho interesse ad usare quotidianamente, quindi non so aiutarti.
 
  • Mi piace
  • Love
Reazioni: --- Ra --- e 0xbro
Ultima modifica:
Sì, per il calcolo parallelo. Gli algoritmi moderni cercano di ostacolare questo trend perché con le cryptocurrencies in auge non è così difficile procurarsi hardware "spacca hash". Per esempio, scrypt funziona tipo bcrypt (i.e., hai un parametro per renderlo più veloce o più lento) solo che oltre a configurare la lentezza dell'algoritmo puoi configurare anche la pesantezza: non puoi testare tante password contemporaneamente perché non ti basta la (V)RAM.


Quel bcrypt_sha256 non è altro che sha256 applicato prima di bcrypt. Fai i dovuti test e verifica tu stesso. Il motivo per cui si usa è dovuto al fatto che bcrypt ha un limite sulla dimensione del plaintext. Ci rimetti un po' in sicurezza perché le fragilità di sha256 adesso si riflettono parzialmente anche su bcrypt, ma ci guadagni qualcosina in flessibilità. Il fatto che questo web framework di cui parli scelga di fare questa roba piuttosto che dire agli user di non usare password ridicolmente lunghe (oltre 72 caratteri) dovrebbe farti capire che gli errori dietro le quinte non sono poi così fuori discussione. Immagino che i programmi citati da 0xbro non supportino nativamente bcrypt_sha256 proprio perché non è una tecnica particolarmente sensata dal punto di vista della sicurezza. Detto questo, non dovrebbe essere particolarmente difficile combinare due programmi, le utilities di unix si basano molto sul concetto "l'output di uno dev'essere l'input dell'altro", altrimenti cerca qualcosa di prefabbricato in rete. Non è roba che ho interesse ad usare quotidianamente, quindi non so aiutarti.
Hai sollevato una questione interessante, hai ragione sul fatto che combinare due hash non significhi necessariamente aumentare la sicurezza. Il web framework a cui mi riferisco è principalmente Django, che permette di utilizzare tra gli altri bcrypt per le password tramite l algoritmo bcrypt_sha256 (come si può evincere direttamente dalla documentazione). Conosco Django e ti garantisco che grazie alla grande comunità che c' è dietro è parecchio sicuro e che gli errori vengono risolti in fretta. Forse utilizzano bcrypt_sha256 anziché bcrypt per il limite di lunghezza delle password che hai menzionato. Però tralasciando la sicurezza matematica della cosa, penso che in questo caso utilizzare due hash renda la vita più difficile al attaccante, dato che per l' appunto c' è da "ingegnarsi" senza trovare soluzioni già pronte. So che bcrypt_sha256 non è altro che una combinazione dei due algoritmi, ma la lentezza per decodificarlo con software che si trovano in giro è molta di più, dato che al posto di partire da una semplice wordlist e tramite bcrypt paragonare ciascuna parola con la password codificata, in pratica bisogna partire da possibili password hashed con sha256 e solo in seguito utilizzare bcrypt (come nella funzione che ho riportato nell' altra risposta). In più c'è il vantaggio che l' algoritmo risulta più lento rallentando di conseguenza i tentativi a forza bruta per il login. Oltre a questa combo Django combina anche PBKDF2 e bcrypt, perciò è una soluzione che adottano. Tralasciando per l' appunto la matematica per quanto riguarda collisioni di cui non posso parlare granché, penso che utilizzare programmi in maniera combinata (o anche uno singolo) per decodificare bcrypt_sha256 sia molto più lento che utilizzarne uno per bcrypt (a causa del uso del doppio hash), quindi in tal senso credo che bcrypt_sha256 contro il solo bcrypt abbia i suoi vantaggi. Secondo te?
Ieri avevo guardato sia per Hashcat che John e non mi pare supportino tale combo di algoritmi. Non so se esistano altri tool che lo facciano, se sì non ne sono a conoscenza, ma vista la specificità penso che la cosa migliore sia fare come ha detto st3ve
Grazie, ho notato anche io che non si trovano programmi che fanno la combo. Mi sa che la cosa migliore sia combinare due programmi come ha detto St3ve.
 
Hai sollevato una questione interessante, hai ragione sul fatto che combinare due hash non significhi necessariamente aumentare la sicurezza. Il web framework a cui mi riferisco è principalmente Django, che permette di utilizzare tra gli altri bcrypt per le password tramite l algoritmo bcrypt_sha256 (come si può evincere direttamente dalla documentazione). Conosco Django e ti garantisco che grazie alla grande comunità che c' è dietro è parecchio sicuro e che gli errori vengono risolti in fretta. Forse utilizzano bcrypt_sha256 anziché bcrypt per il limite di lunghezza delle password che hai menzionato. Però tralasciando la sicurezza matematica della cosa, penso che in questo caso utilizzare due hash renda la vita più difficile al attaccante, dato che per l' appunto c' è da "ingegnarsi" senza trovare soluzioni già pronte. So che bcrypt_sha256 non è altro che una combinazione dei due algoritmi, ma la lentezza per decodificarlo con software che si trovano in giro è molta di più, dato che al posto di partire da una semplice wordlist e tramite bcrypt paragonare ciascuna parola con la password codificata, in pratica bisogna partire da possibili password hashed con sha256 e solo in seguito utilizzare bcrypt (come nella funzione che ho riportato nell' altra risposta). In più c'è il vantaggio che l' algoritmo risulta più lento rallentando di conseguenza i tentativi a forza bruta per il login. Oltre a questa combo Django combina anche PBKDF2 e bcrypt, perciò è una soluzione che adottano. Tralasciando per l' appunto la matematica per quanto riguarda collisioni di cui non posso parlare granché, penso che utilizzare programmi in maniera combinata (o anche uno singolo) per decodificare bcrypt_sha256 sia molto più lento che utilizzarne uno per bcrypt (a causa del uso del doppio hash), quindi in tal senso credo che bcrypt_sha256 contro il solo bcrypt abbia i suoi vantaggi. Secondo te?

Grazie, ho notato anche io che non si trovano programmi che fanno la combo. Mi sa che la cosa migliore sia combinare due programmi come ha detto St3ve.
Svariate volte ho utilizzato anche io Hashcat e John, posso confermare che non supportano le combo.
 
Non è inefficiente, è proprio inutile. Il salt si usa proprio per impedire gli attacchi con le rainbow tables.
Forse non è del tutto inutile utilizzare una rainbow table in questo caso, dato che il salt viene applicato soltanto con bcrypt e non con sha256 (nella funzione che ho riportato sopra). Perciò si potrebbe partire da una serie di password precedentemente hashed con sha256 ed inseguito applicare bcrypt
 
Però tralasciando la sicurezza matematica della cosa, penso che in questo caso utilizzare due hash renda la vita più difficile al attaccante, dato che per l' appunto c' è da "ingegnarsi" senza trovare soluzioni già pronte. So che bcrypt_sha256 non è altro che una combinazione dei due algoritmi, ma la lentezza per decodificarlo con software che si trovano in giro è molta di più, dato che al posto di partire da una semplice wordlist e tramite bcrypt paragonare ciascuna parola con la password codificata, in pratica bisogna partire da possibili password hashed con sha256 e solo in seguito utilizzare bcrypt (come nella funzione che ho riportato nell' altra risposta).
Non avere un programma già pronto all'uso è un vantaggio irrisorio. Una volta che modificheranno hashcat o john the ripper sei al punto di partenza. Può essere una spina nel fianco per gli hacker della domenica, ma è un ostacolo da poco per chi è un po' più esperto.

In più c'è il vantaggio che l' algoritmo risulta più lento rallentando di conseguenza i tentativi a forza bruta per il login.
La velocità dell'algoritmo è praticamente irrilevante perché bcrypt è pensato per essere reso più lento o più veloce a seconda delle proprie esigenze. Quando chiami la funzione di hash hai un parametro da scegliere (un numero) che indica il costo dell'algoritmo. Passando da un costo c a un costo c+1 il numero di rounds raddoppia. Di norma si fanno migliaia di rounds e, chiaramente, se stai già facendo 4096 iterazioni (valore fissato in Django) di blowfish non è quella singola iterazione di sha a rallentarti. Se si fossero preoccupati che bcrypt non è abbastanza lento avrebbero semplicemente alzato il parametro costo.

Combinare funzioni di hash per aumentare la sicurezza è uno di quegli esempi del vero significato di don't roll your own crypto: prima di fare cose fuori dall'ordinario devi informarti bene, perché c'è il serio rischio di ottenere side effects indesiderati. In questo caso non penso che si siano inventati niente, però di sicuro non hanno aumentato la sicurezza perché dal punto di vista matematico l'hanno addirittura diminuita. È comunque un algoritmo sicuro, ma è meno sicuro di bcrypt liscio. Chiaramente le loro intenzioni erano altre, secondo me non volevano vietarti di inserire password più lunghe di 72 caratteri (scelta che personalmente non condivido).

Forse non è del tutto inutile utilizzare una rainbow table in questo caso, dato che il salt viene applicato soltanto con bcrypt e non con sha256 (nella funzione che ho riportato sopra). Perciò si potrebbe partire da una serie di password precedentemente hashed con sha256 ed inseguito applicare bcrypt
Sì, volendo puoi procedere in questo modo, ma visto che stavi parlando di "riflettere a livello teorico" sappi che l'ostacolo vero è bcrypt perché sha256 è bello veloce. A livello pratico, io procederei a mo' di catena di montaggio. Con il codice discusso qui e il mio laptop vecchio di 7 anni riesco facilmente a calcolare qualche milione di sha256 al secondo (sulla cpu e in single core!). Il problema è che se la password è robusta non c'è niente che tu possa fare: bcrypt è sicuro.