Il controllo sulla qualita' delle password utilizzate su un Database migliora in modo significativo la sicurezza di un sistema. In questa pagina descriviamo come controllare le password sui piu' diffusi DB relazionali (Oracle, MySQL, PostgreSQL) con un approccio Brute Force ovvero provandole tutte! Il programma utilizzato per il controllo e' John the Ripper. John the Ripper, un noto tool per la sicurezza dei sistemi, e' uno degli strumenti piu' adatti per la decrittazione delle password poiche' utilizza sia liste di password conosciute, sia l'approccio combinatorio sfruttando algoritmi particolarmente efficienti.
Un accesso diretto ai database per trovare le utenze meno sicure
provando le utenze/password piu' comuni
e' tecnicamente fattibile ma molto lento ed intrusivo.
L'idea di base utilizzata in questa pagina
e' quella di scaricare su file
l'elenco delle utenze e delle password (crittografate) e poi controllarle
su un altro sistema con John the Ripper...
L'esecuzione di John the Ripper su un sistema Unix e' banale:
Se una password viene scoperta da John in pochi secondi appartiene ad un dizionario o e' una password veramente troppo facile: e' una password da cambiare! John continua a provare password sempre piu' complesse e lunghe fino a che non viene interrotto. Password lunghe e basate su algoritmi complessi possono richiedere mesi o anni per essere ritrovate da John: queste sono password buone.
John si aspetta un file con il vecchio formato /etc/password, quindi gli script che seguono
servono a preparare tale file partendo da un database relazionale.
I principali database relazionali mantengono le password in modo crittografato
su un catalogo di sistema.
Poiche' ogni DB utilizza oggetti differenti gli esempi sono specifici per
ogni tipologia di DB:
Oracle,
MySQL,
Postgres,
SQL Server,
...
e, per non farci mancare proprio nulla, diamo anche un'accenno ai principali
CMS.
Oltre al famigerato SYS/CHANGE_ON_INSTALL
le password di default di Oracle e dei sui tool sono molte
ma possono essere controllate facilmente con la query
descritta in questo documento
[NdA e' un problema non piu' presente con le versioni piu' recenti di Oracle].
Fino alla versione 11g Oracle calcola l'hash delle password con un triplo DES ed un salt e le mantiene
nella tabella di sistema dba_users. Il numero massimo di tentativi di accesso errati
e' regolato dal valore FAILED_LOGIN_ATTEMPTS.
Per un controllo piu' completo delle password utilizzate dagli utenti Oracle e' possibile generare un file adatto a John con:
set heading off set feedback off set pages 0 set echo off spool /tmp/mypass.txt select username||':'|| password from dba_users where account_status='OPEN'; spool off
Poiche' il formato dell'hash di Oracle e' simile ad altri, per indicare a John la formattazione corretta va utilizzato il comando:
Dalla versione 11g le password Oracle sono case sensitive, l'hash non e' visibile sulla vista DBA_USERS, l'algoritmo di crittografazione ed il salt sono piu' complessi (da 3DES a SHA-1) ed il salt non dipende piu' dal nome dell'utente ma viene generato. Ecco come estrarre il file per le utenze su cui e' stato utilizzato il nuovo algoritmo (da analizzare con format=oracle11):
set heading off set feedback off set pages 0 set lines 132 spool /tmp/mypass.txt select name||':'|| substr(spare4,3) from SYS.USER$ where spare4 is not null; spool offUn trucco molto noto ai DBA Oracle, quando non conoscono una password, e' salvare la vecchia password, impostarne una nuova conosciuta, eseguire le attivita' desiderate ed infine ripristinare la vecchia password con:
Dalla versione 12 le password utilizzano SHA-2 (SHA-512). Per compatibilita' gli utenti possono utilizzare password in versioni diverse ma ovviamente le versioni piu' recenti sono molto piu' sicure. Per verificare la versione della password si utilizza la query:
SELECT USERNAME, PASSWORD_VERSIONS FROM DBA_USERS;
MySQL mantiene le utenze, gli host di provenienza e l'hash delle password nella tabella mysql.user.
Le versioni precedenti alla 4.1 utilizzano un algoritmo di crittografazione piuttosto modesto (64 bit),
senza salt e con molte collisioni (quindi poco sicuro anche contro gli attacchi brute force).
La password crittografata viene memorizzata in una stringa di 16 caratteri.
Dalla versione 4.1 e' utilizzato un doppio SHA-1 ma, anche in questo caso, senza salt:
SELECT concat('*', SHA1(UNHEX(SHA1("secret"))));
La funzione di crittografazione e' richiamabile da SQL: password() [NdE
l'algoritmo si sceglie con SET old_passwords = n; e si utilizza old_password()
per le versioni precedenti alla 4.1].
La password crittografata viene memorizzata in una stringa di 41 caratteri.
L'assenza di un salt rende abbastanza semplice il confronto con hash precalcolati e l'identificazione di utenze
con la stessa password...
Dalla versione 5.6 e' disponibile il plugin
sha256_password che utilizza SHA-2 per l'hashing, un salt ed un differente protocollo per l'autenticazione,
tuttavia tale plugin non e' molto utilizzato nelle versioni 5.x (il default e' mysql_native_password).
La password crittografata viene memorizzata in una stringa di 70 caratteri.
Dalla versione 5.7 sono state desupportate le password pre-4.1, e' stata deprecata la funzione password()
e la colonna password e' stata chiamata authentication_string.
Sulle versioni Enterprise e sui fork sono disponibili plugin di autenticazione esterni (eg. PAM).
Per bloccare eventuali attacchi brute force
MySQL mantiene una tabella di cache degli host interna e mette in blackout l'IP del client raggiunto un certo numero
di tentativi falliti (max_connect_errors).
A partire dalla versione 5.6 (o prima su alcuni fork) e' possibile un controllo diretto degli errori
di connessione (performance_schema.host_cache), in precedenza tale informazione non era disponibile da SQL.
Per ottenere un file per John con MySQL il codice SQL e':
select user, authentication_string, host from mysql.user into outfile '/tmp/mypass.txt' fields terminated by ':' lines terminated by '\n';
Con la versione 8.0 cambiano ancora le cose (in principio un client 5.7 di default non riusciva
a collegarsi alla versione 8.0) e viene utilizzato il plugin caching_sha2_password.
L'authentication_string ha la forma $A$005$SaltHash con un Salt di lunghezza 20
ed un Hash di lunghezza 43, la stringa puo' contenere caratteri speciali.
Poiche' il Salt e' sempre diverso due password uguali sono difficili
da riconoscere e non possono essere costruiti dizionari di password craccate.
Con la versione 8.0 la funzione password() non e' piu' disponibile e non c'e' un modo
semplice per ottenere l'hash da SQL.
Con la versione 8.4 viene deprecato il plugin mysql_native_password che quindi verra' rimosso in futuro.
Al momento, John non e' in grado di effettuare l'attacco...
ma come alternativa c'e' Hashcat:
SELECT user, CONCAT('$mysql', SUBSTR(authentication_string,1,3), LPAD(CONV(SUBSTR(authentication_string,4,3),16,10),4,0),'*', INSERT(HEX(SUBSTR(authentication_string,8)),41,0,'*')) AS hash FROM mysql.user WHERE plugin = 'caching_sha2_password' AND authentication_string NOT LIKE '%INVALIDSALTANDPASSWORD%';
La tabella PostgreSQL pg_user, tipicamente accessibile a tutti gli utenti, contiene la lista degli utenti ma non le password... E' necessario utilizzare la tabella pg_shadow, accessibile solo agli amministratori, che riporta anche l'hash della password. Le password in Postgres vengono crittografate con MD5 (per essere precisi viene crittografata la concatenazione della password e del nome utente: SELECT 'md5'||MD5(passwd||usename) . Nell'handshake di autenticazione tra client e server viene scambiato l'hash in chiaro o l'MD5 con salt dell'hash a seconda dell'impostazione presente nel file di configurazione hba.conf (password vs md5). Nelle versioni piu' recenti di Postgres sono presenti le tabelle pg_roles e pg_authid con una strutturazione piu' completa dei diritti di accesso e le autorizzazioni [NdA dalla versione 8.1 del 2005]. Con la versione 10.1 PostgreSQL supporta anche l'algoritmo scram-sha-256 per lo scambio e la memorizzazione della password che e' molto piu' robusto dal punto di vista crittografico e non e' attaccabile facilmente perche' utilizza salt differenti. Il formato di memorizzazione e' SCRAM-SHA-256$Iteration count:Salt$StoredKey:ServerKey in base64. Tuttavia tale algoritmo non e' supportato dalle release precedenti e quindi sara' necessario un po' di tempo prima che venga utilizzato in modo significativo. E' anche possibile utilizzare il formato UNENCRYPTED... ma per fortuna non lo si usa mai. Il codice SQL seguente e' valido per tutte le versioni di Postgres (eccetto per le password in SCRAM-SHA-256) ed estrae un file adatto all'analisi con John (formato raw-md5):
\pset tuples_only \pset format unaligned \pset fieldsep ':' \pset recordsep '\n' \o /tmp/mypass.txt select usename, substr(passwd,4) from pg_shadow where passwd like 'md5%';
SQL Server mantiene i pwdhash nel master.mdf. Formati e tabelle dipendono dalla versione...
SQL Server utilizza SHA-1 (format=mssql),
la query per ottenere l'elenco delle utenze e degli hash e':
select name, password from master..sysxlogins where password IS NOT NULLSe la visualizzazione dei valori esadecimali dell'hash della password non e' corretta si puo' utilizzare la funzione master.dbo.fn_varbintohexstr(password).
select name, password from master.dbo.sysxlogins where password IS NOT NULLCon SQL Server 2008 la query e' invece:
select name, password_hash from master.sys.sql_logins
DB2 si appoggia al sistema operativo per l'autenticazione delle utenze. Le utenze sono quindi contenute nel file /etc/passwd e tipicamente iniziano con db2 (eg. db2fenc1, db2inst1, dasusr1). L'analisi con John non richiede quindi nulla di diverso rispetto ad un controllo password su Unix, per restringere il file ai solo utenti interessati e' sufficiente un grep -i ^db2
SQLite non dispone di alcun meccanismo di autorizzazione ed utilizza i normali permessi del sistema operativo sul file di database. Alcune applicazioni utilizzano file di database crittografati (eg. Whatsapp con un AES-192), in questi casi e' necessario prima ottenere il file di database in chiaro e quindi accedere con un client SQLite.
CouchDB mantiene le eventuali utenze in _config, le password sono crittografate con SHA-1 ed hanno un salt basato sullo UUID. Per default e' tutto libero a tutti...
Anche su MongoDB per default l'autenticazione non e' abilitata (parametro --auth) [NdA lo era fino alla 3.0]. Le utenze sono definite per database, le utenze del database admin possono agire su tutti i database. L'elenco delle utenze e delle password si ottiene con db.system.users.find().pretty(). L'algoritmo per la memorizzazione della password e' MD5(utente:mongo:password): senza salt! Nella versione 2.4 l'algoritmo e' SHA256(MD5). Con la versione 3.0 e' stato introdotta l'autenticazione SCRAM (Salted Challenge Response Authentication Mechanism) notevolmente piu' sicura, l'implementazione di Mongo utilizza SHA-1. Con la versione 3.6 la vecchia autenticazione MONGODB-CR e' stata deprecata. Dalla versione 4.0 viene utilizzato lo SHA-256 che e' ora il default.
ClickHouse utilizza un file del sistema operativo
con l'elenco di username e password: /etc/clickhouse-server/users.xml.
Le password possono essere anche in chiaro...
ma tipicamente vengono crittografate in SHA256 dal DBA.
E' anche possibile utilizzare il doppio SHA-1 come con MySQL.
E' possibile impostare l'utenza utilizzando il comando con l'hash della password.
Per calcolare l'hash su un sistema esterno il comando e':
echo -n "MySecurePassword" | sha256sum | tr -d '-'
Dall'introduzione dell'RBAC (eg. praticamente completa dalla versione 20.4)
la gestione degli accessi e della sicurezza e' molto sofisticata in ClickHouse.
Tra le altre e' presente una tabella system.users e le password sono riportate nei
file /var/lib/clickhouse/access/xxx.sql in forma crittografata (default SHA256).
La teoria degli algoritmi di crittografia e' complessa e richiede basi matematiche significative...
quindi non sarebbe il caso di approfondirla!
Peroo' qualcosa bisogna conoscerlo comunque...
servono algoritmi sicuri per
per la verifica d'integrita', lo scambio di chiavi, per l'autenticazione, per la cifratura simmetrica.
La prova del nove, quella che si impara alle elementari e che per i numeri binari diventa la parita',
e' un tipico algoritmo per il controllo degli errori ovvero per la verifica di integrita'.
Le serrature che proteggono le porte di casa sono un altro esempio noto a tutti:
se richiedono chiavi piu' complesse sono piu' difficili da scassinare.
Lo stesso vale con le chiavi binarie: piu' sono lunghe e piu' sono sicure!
Per passare un segreto tra amici? Basta usare qualcosa che si conosce solo tra noi.
Per nascondere qualcosa il modo piu' semplice e' quello di metterla dove ve ne sono molte
altre simili; cosi' con un numero binario basta moltiplicarlo per un altro o per molti altri.
Per avere una chiave binaria sicura... basta utilizzarne una piu' lunga del messaggio da cifrare
ed usarla una sola volta; anzi questa e' l'unico tipo di chiave veramente sicura.
Pero' attualmente ci si accontenta che non si riesca far bollire tutta l'acqua della terra.
Beh, ho semplificato parecchio, ma le basi sono queste!
La tabella seguente riassume gli algoritmi di hash delle password utilizzati per i database descritti in questa pagina:
Algoritmo | Bit | HEX | Database | Note |
DES | 56 | 14 | ||
custom | 64 | 16 | MySQL < 4.1 | old_password() |
MD5 | 128 | 32 | PostgreSQL MongoDB | |
Triple DES | fino a 168 | Oracle | ||
SHA-1 | 160 | 40 | MySQL Oracle >= 11 SQL Server CouchDB | Memorizza il dato con un '*' iniziale (41 HEX) =password() |
SHA-2 | da 224 a 512 | MySQL >= 5.6 Oracle >= 12.1 PostgreSQL >= 10.1 MongoDB >= 2.4 ClickHouse | Memorizza la password crittografata in una stringa di 70 caratteri
Utilizza una stringa di 64 caratteri =sha256sum |
Non ci sono DB2, SQLite perche' dipendono dal sistema operativo... Su Unix/Linux l'algoritmo dipende dal salt presente in /etc/shadow: se inizia con $1$ usa MD5, se inizia con $5$ usa SHA-256, se inizia con $6$ usa SHA-512. Negli altri casi utilizza il DES.
Naturalmente nel valutare la robustezza di una base dati nel confronto ad un attacco bruteforce non e' importante solo l'algoritmo utilizzato ma anche la scelta del SALT e le policy sulla qualita' della password (comprese quelle di default). Ma per ora basta cosi!
Le tecniche riportate in questo documento sono utilizzabili anche su molte applicazioni che utilizzano una base dati. Le applicazioni che subiscono il maggior numero di attacchi sono tipicamente disponibili su web ed implementate con un'architettura LAMP... ci e' parso utile riportare qualche dettaglio sulle piu' diffuse [NdA ovviamente con il LAMP la base dati e' tipicamente MySQL].
Se un'applicazione non e' robusta un possibile attacco puo'
essere svolto in due passi: con un attacco di tipo SQL_injection
rivolto all'aplicazione on-line si ottiene
l'elenco delle utenze e delle password, quindi si analizza off-line
l'elenco con un approccio brute force nel tempo
necessario a decrittografare le password principali.
Ottenute le utenze/password
ci si collega poi normalmente all'applicazione
e si pubblica tutto quello che si vuole...
Vediamo alcuni dei piu' diffusi CMS:
I nomi dei database possono cambiare, in particolare se sullo stesso server sono ospitati piu' applicazioni, e possono essere utilizzati algoritmi custom per l'autenticazione... ma la maggioranza delle installazioni utilizza le modalita' indicate sopra.
Per analizzare i file ottenuti dai DB va utilizzata la versione arricchita Jumbo di John the Ripper oppure la versione Pro (a pagamento). Entrambe le versioni citate contengono gli algoritmi di crypt necessari. Il sito ufficiale dove trovare il sofware e' ospitato da Openwall; per macos si puo' utilizzare Homebrew: basta lanciare il comando brew install john-jumbo .
Un attacco diretto di tipo Brute Force ad un Database non e' praticamente realizzabile con le versioni attuali dei diversi DB poiche' richiederebbe troppo tempo (i DB rallentano o bloccano appositamente i successivi tentativi di connessione in caso di password errata). Il metodo descritto in questo documento richiede un accesso privilegiato sul DB solo per un breve periodo (il tempo di lanciare una select) e consente un approccio Brute Force per decriptare le password utilizzando un altro server per tutto il tempo necessario.
Una precisazione... Con un attacco Brute Force non e' corretto dire che le password vengono decrittografate: gli algoritmi utilizzati per hash delle password non sono reversibili. Semplicemente vengono provate tutte le possibili combinazioni di caratteri calcolando ogni volta il valore crittografato. Quando si trova una corrispondenza si e' ottenuta la password. Password corte o basate su liste note vengono riconosciute in pochi secondi. Una buona password richiede mesi per essere "decrittografata" su un HW normale. Comunque nessuna password e' in grado di resistere per un tempo indefinito: per questo le password vanno cambiate!
John utilizza ancora il vecchio formato del file /etc/passwd. Ora su linux le password vengono mantenute sul file /etc/shadow con il fomato $id$salt$hashed dove il primo campo indica l'algoritmo di cifratura: $1$ per MD5, $2a$ per Blowfish, $2y$ per Blowfish, $5$ per SHA-256, $6$ per SHA-512.
Altri documenti di questo tipo su questa pagina
Titolo: Controllare le password su Database
Livello: Hack
Data:
31 Ottobre 2012 🎃 Halloween
Versione: 1.0.10 - 14 Febbraio 2024 ❤️ San Valentino
Autore: mail [AT] meo.bogliolo.name