Se ti piace andare ai WordCamp come faccio io, probabilmente hai già sentito questo: “WordPress password hashing non è sicuro”, o nella versione più tecnica: “…perché è basato su md5”.
Vero o no, un hashing password forte è fondamentale per un grande ecosistema come quello di WordPress, che è sempre stato un bersaglio succosa per gli hacker. Così, ho deciso di dare un’occhiata più da vicino al sistema di hashing e cercare di rompere gli hash di WordPress da zero!
Comprensione degli hash delle password di WordPress
Ho iniziato a fare un po ‘ di googling e ho scoperto che la maggior parte delle informazioni là fuori è generica e confusa. Molti riferimenti alle librerie PHP utilizzate (hash portatile da phpass), ma niente di veramente concreto.
Ho deciso di adottare un approccio diverso partendo da un presupposto:
l’hashing è un processo a senso unico, ma WordPress è in qualche modo in grado di autenticare gli utenti che corrispondono all’input della password con l’hash memorizzato nel database
Da lì, ho iniziato a controllare il codice e ho trovato la prima funzione interessante: wp_check_password($password,$hash)
che confronta la password di testo normale con l’hash e restituisce true
1// presume the new style phpass portable hash. 2if ( empty( $wp_hasher ) ) { 3 require_once ABSPATH . WPINC . '/class-phpass.php'; 4 // By default, use the portable hash from phpass. 5 $wp_hasher = new PasswordHash( 8, true ); 6} 7 8$check = $wp_hasher->CheckPassword( $password, $hash ); 9 10/** This filter is documented in wp-includes/pluggable.php */ 11return apply_filters( 'check_password', $check, $password, $hash, $user_id );
Passando attraverso il codice, ci porta rapidamente a CheckPassword
, crypt_private
e encode64
che fondamentalmente è dove avviene la magia.
Per farla breve, crypt_private($password, $stored_hash)
ri-hash la password prima che venga confrontata con l’hash memorizzato. Se corrispondono, la password è corretta e l’autenticazione continua. Il che significa che possiamo anche usare quella funzione per rompere l’hash.
Hash anatomy
Questo è un hash di WordPress:
1$P$BnPVO4gP9JUMSAM1WlLTHPdH6EDj4e1
Per semplicità, assumeremo che il sito usi PHP > 5 e il più recente hash portatile phpass, che è la configurazione più comune.
I primi 3 caratteri $P$
sono un ID, che dice al sistema quale tipo oh hash abbiamo.
Il numero di caratteri 3 (contando da 0) viene utilizzato per determinare quante volte md5 () deve elaborare la stringa di input.
I caratteri da 4 a 12 nPVO4gP9
sono il sale, che è una stringa casuale aggiunta alla password prima dell’hashing, per dargli più casualità. Ad esempio, se la tua password è admin, viene attivata su nPVO4gP9admin e quindi hash.
La parte rimanente dell’hash JUMSAM1WlLTHPdH6EDj4e1
è la casualità reale, generata dalla password salt+passata in una funzione encode64
non documentata, che esegue alcune operazioni bit a bit sulla stringa di input e restituisce un output di 22 caratteri.
Non è così chiaro, uh? Resta con me.
Finora sappiamo:
- la prima parte dell’hash è un fisso id
- il secondo è un singolo carattere usato come contatore, anche fisso
- il terzo è il sale, legato alla password
- l’ultimo è casuale, generato da “sale+pass’ trattati da encode64 funzione
1
Così, siamo in grado di ri-scrivere la logica di sale+password hash X volte e passato nel encode64 – per eseguire un dizionario o un attacco bruteforce, e ottenere lo stesso ‘ultimo tratto’ di hash, e che sarebbe un successo hash crack!
In uno scenario di vita reale, e gli hacker sarebbero più interessati a trovare le password deboli perché sono più probabilità di essere riutilizzati, invece di quelli casuali che sono spesso generati da un gestore di password e così site-specific.
Riscrivendo encode64 usando golang
Ho deciso di andare con golang perché è molto veloce e abbiamo bisogno di tutta quella velocità per calcolare dizionari di password di grandi dimensioni.
Proprio come nella procedura di crittografia di WordPress, lo script prende l’hash e isola il sale, quindi compone la password (salt+pass) tentando ogni password nel dizionario e md5-hashing X numero di volte, e abbiamo visto come X è determinato.
1itoa64 := "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" 2hashloop := 1 << strings.Index(itoa64, string(hash)); // char n. 4 in the hash
Fatto ciò, esegue una serie di operazioni bit a bit su byte ottenuti usando ord () in PHP, di cui non abbiamo bisogno in golang in quanto abbiamo già valori di byte:
1itoa64 & 0x3f]
A questo punto, mi sono reso conto che non abbiamo nemmeno bisogno di trovare l’intera stringa. È sufficiente abbinare alcuni caratteri per abbinare l’intera password e rendere lo script più performante! Quindi, a causa della natura del processo di hashing, se char n. 0 / 4 / 8 … corrisponde, la password passata come input è corretta.
E BOOM, abbiamo rotto l’hash:
1wphashcrash '$P$BnPVO4gP9JUMSAM1WlLTHPdH6EDj4e1' Dev/wphashcrash/dict.txt 2$P$BnPVO4gP9JUMSAM1WlLTHPdH6EDj4e1 admin 32020/05/30 12:44:15 Executed in 0.000221
Il codice è come al solito su GitHub – https://github.com/francescocarlucci/wphashcrash – e al momento, lo script è un POC e ottiene solo un singolo hash della password come input e il percorso del dizionario della password.
Ho già delineato nel readme alcuni miglioramenti che sarebbe bello avere e probabilmente aggiungerò in futuro.
Se vuoi davvero provare a hackerare un elenco di hash, puoi biforcarlo o semplicemente avvolgere lo script in un ciclo bash for.
Considerazioni di sicurezza
A questo punto, penso che sia abbastanza chiaro che se un utente malintenzionato ottiene l’accesso al database su un sito WordPress, lui/lei può fondamentalmente rompere ogni password debole.
Questo sfortunato, soprattutto perché:
- SQL Injection non è raro in WordPress plugins
- RCE sarà anche più probabile concedere l’accesso al DB
- WordPress è un ecosistema di un territorio enorme per freelancer lavoro ed è molto comune trovare vecchi/inutilizzati wp-admin e account FTP, creato per temporanee esigenze
Quest’ultimo punto, combinato con enumerazione degli utenti che WordPress problema comune, lascia aperta la porta per accedere al DB usando furto/dato in pegno le credenziali di amministratore.
In cima a questo, molti siti web non impongono password complesse per non danneggiare l’esperienza utente, e ho esperienza diretta in uno qualsiasi dei punti menzionati.
Una volta che un sito web è compromesso, avere hash deboli memorizzati consente agli aggressori di compromettere altri account utente su altri siti, se tendono a riutilizzare le password e sappiamo che lo fanno.
Abbiamo soluzioni?
La mia ricerca non è andata così lontano, volevo solo vedere come rompere gli hash di WordPress.
Bcrypt è noto per essere un forte metodo di hashing rispetto al md5, e c’è un plug-in esistente che utilizza bcrypt e sostituisce tutte le principali funzioni necessarie a gestire le password:
- wp_check_password()
- wp_hash_password()
- wp_set_password()
E, naturalmente, è possibile scrivere la vostra propria soluzione come sviluppatore, ma credo che questo sia un problema da risolvere a livello di base per puntare ad un grande adozione.
Nota finale
Ci sono molti strumenti per decifrare gli hash, come Hashcat e John The Ripper che possono essere ancora più performanti, ma ancora una volta, lo scopo di questa ricerca è capire la struttura di hash di WordPress e cracking da zero.
Grazie per la lettura.
frenxi