Ogni tanto una vulnerabilità ricorda quanto la nostra infrastruttura digitale dipenda da librerie fondamentali che diamo per scontate. È il caso di HollowByte, una falla di tipo Denial of Service scoperta dal team di ricerca di Okta in OpenSSL: con un payload malevolo di appena 11 byte, un attaccante remoto e non autenticato può costringere un server ad allocare quantità di memoria completamente sproporzionate, prima ancora che l’handshake di sicurezza abbia inizio.
Per chi gestisce server web, database o qualsiasi servizio che parla TLS — quindi, in pratica, quasi tutti i sistemisti — vale la pena capire come funziona e, soprattutto, cosa fare subito.
Fidarsi ciecamente dell’header
L’handshake TLS comincia con un messaggio ClientHello incapsulato in un record. Ogni messaggio di handshake porta un header di 4 byte che dichiara quanto sarà grande il corpo del messaggio in arrivo.
Nelle versioni vulnerabili di OpenSSL, il buffer di ricezione viene allocato sulla base di quella lunghezza dichiarata dall’attaccante, prima ancora che i dati siano effettivamente arrivati. La catena di chiamate è semplice quanto pericolosa:
Lettura header (4 byte)
→ grow_init_buf()
→ OPENSSL_clear_realloc()
→ malloc(dimensione_dichiarata_dall_attaccante)
Poiché a questo stadio non esiste alcuna validazione del payload, un header che dichiara una lunghezza di 3 byte può far allocare fino a 131 KB basandosi solo sulla dichiarazione del pacchetto, che nessuno verifica. Il worker thread resta poi bloccato ad attendere dati che non arriveranno mai.
L’effetto moltiplicatore: la frammentazione della memoria
Tenere connessioni aperte per esaurire i thread è un trucco vecchio quanto Slowloris. Quello che rende HollowByte più insidioso è l’interazione con la gestione della memoria di glibc (GNU C Library).
Quando una connessione dell’attaccante si chiude, OpenSSL libera il buffer — ma glibc non restituisce immediatamente al sistema operativo le allocazioni di dimensione piccola o media: le trattiene per un possibile riutilizzo. Lanciando ondate di connessioni con dimensioni dichiarate casuali, un attaccante impedisce all’allocatore di riutilizzare in modo efficiente quei blocchi liberati. L’heap si frammenta pesantemente e la Resident Set Size (RSS) del processo cresce in modo continuo.
Il punto critico è questo: anche dopo che l’attaccante si è disconnesso, il processo resta con un footprint di memoria permanentemente gonfiato. L’unico modo per recuperarla davvero è riavviare il servizio.
I numeri dei test
Il team di Okta ha testato istanze OpenSSL patchate e non patchate dietro NGINX, sotto diverse condizioni di carico:
- In un ambiente con 1 GB di RAM, il server non patchato è stato terminato dall’OOM killer dopo aver accumulato 547 MB di memoria frammentata e inutilizzabile
- In un ambiente con 16 GB di RAM, l’attacco ha bloccato il 25% della memoria totale del sistema, restando sotto la soglia massima di connessioni consentite — il che significa che le classiche difese basate su rate limiting delle connessioni non fermano questo attacco
Dato che OpenSSL è incorporato ovunque, l’impatto potenziale copre web server (Apache, NGINX), runtime di linguaggi (Node.js, Python, Ruby, PHP) e database (MySQL, PostgreSQL) che si appoggiano alla libreria per TLS.
La correzione: crescita incrementale del buffer
Il team OpenSSL ha risolto il problema passando a una crescita incrementale del buffer (merge delle pull request #30792, #30793 e #30794): invece di fidarsi della lunghezza dichiarata nell’header, il buffer cresce solo quando i byte arrivano effettivamente sulla connessione. Una dichiarazione senza seguito ora non costa più nulla al server.
Il fix è stato incluso silenziosamente nella release OpenSSL 4.0.1, con backport altrettanto silenziosi sulle versioni:
3.6.3
3.5.7
3.4.6
3.0.21
Un dettaglio non trascurabile: OpenSSL ha trattato la correzione come un semplice hardening fix, senza assegnare un CVE ufficiale, nonostante la gravità dell’impatto. Questo significa che molti scanner di vulnerabilità basati solo su database CVE potrebbero non segnalare l’esposizione: va verificata manualmente la versione installata.
Cosa fare subito
Alcune azioni concrete da mettere in pratica sui vostri sistemi:
- Verificate la versione di OpenSSL su tutti i server esposti:
openssl version -a
- Aggiornate i pacchetti di sistema alla versione patchata più recente disponibile per la vostra distribuzione:
# Debian/Ubuntu
apt update && apt list --upgradable | grep -i openssl
apt install --only-upgrade openssl libssl3
# RHEL/Fedora/Alma
dnf check-update openssl
dnf update openssl
- Non fermatevi al pacchetto di sistema: controllate anche runtime e linguaggi che incorporano una propria build di OpenSSL (build statiche di Node.js, alcune distribuzioni Python, container con immagini “slim” che a volte portano versioni di OpenSSL più vecchie di quelle dell’host)
- Monitorate la RSS dei processi che terminano connessioni TLS, non solo il numero di connessioni attive: un aumento di memoria residente senza corrispondente crescita del traffico è un segnale d’allarme
- Irrigidite i timeout di handshake lato reverse proxy, ad esempio su NGINX:
ssl_handshake_timeout 10s;
client_header_timeout 10s;
- Valutate regole di rate limiting sulle nuove connessioni TLS per IP a livello di firewall o WAF, anche se — come mostrato dai test — da sole non bastano contro questa specifica tecnica
Conclusione
HollowByte è un buon esempio di come un bug apparentemente piccolo — la fiducia cieca in un header di pochi byte — possa diventare un vettore di Denial of Service difficile da rilevare con le metriche di monitoraggio standard, perché resta sotto le soglie di allarme sulla banda e sulle connessioni. La buona notizia è che la correzione è già disponibile e il percorso di mitigazione è chiaro: aggiornare OpenSSL su tutta la superficie esposta, non fidarsi solo dei database CVE per capire se si è vulnerabili, e aggiungere il monitoraggio della memoria residente ai controlli già in essere sui vostri servizi TLS.
Fonte: BleepingComputer e Okta Security.