Salta al contenuto

Blog ·

Perché uno scanner di vulnerabilità deve inviare zero byte

Il confine tra un controllo e un attacco sono i byte messi sul filo. Come trasformare quella promessa in un numero su cui i test possono fallire, con le misure.

Di Provibr

Se gestisce infrastruttura per conto di altri, prima o poi vuole sapere se la porta 3306 su una delle macchine dei suoi clienti sta rispondendo a tutta Internet. È una domanda ragionevole, il cliente vorrebbe la risposta, e nel momento in cui va a guardare è diventato una parte che invia traffico non richiesto a una macchina sotto il contratto di qualcun altro.

Ciò che separa questo da un’intrusione non è la sua intenzione, e non è una casella nelle sue condizioni di servizio. È che cosa ha davvero lasciato il socket. Tutto il resto è un racconto su che cosa ha lasciato il socket.

Di recente abbiamo costruito nel nostro agente un controllo dell’esposizione, con una regola nata come frase in un documento di progettazione: non inviare alcuna query che modifichi, alcun tentativo di accesso, alcuna credenziale predefinita, alcun payload di exploit, alcuna raccolta estesa di banner. Ecco come quella frase è diventata qualcosa su cui un test può fallire, e quanto sono costate in byte le sonde che ne sono uscite.

Una promessa in un commento è una promessa con una scadenza

Il problema della frase qui sopra non è che sia difficile da seguire. È che è facile da seguire per circa sei mesi.

Qualcuno aggiunge un EHLO alla sonda SMTP perché possa rilevare se viene offerto STARTTLS, che è un’informazione davvero utile. I test esistenti continuano a passare, perché fanno asserzioni su ciò che la sonda ha trovato e non su ciò che ha detto. Da quel giorno il software invia comandi di protocollo alle macchine dei clienti, e il documento di progettazione continua a dire che non lo fa. Non è diventato rosso nulla. Non lo diventerà mai.

Perciò l’abbiamo reso un numero dove un numero è possibile. Ogni socket che il controllo apre è avvolto in un flusso che conta i byte scritti, e il totale torna indietro insieme al risultato. Quattro dei sei tipi di sonda hanno una cifra esatta verificata nei test: zero per le due che non inviano nulla, trentadue per SSH, otto per PostgreSQL. Le altre due non possono, perché un client hello e un negotiate SMB variano in lunghezza a seconda di ciò che offrono al loro interno; il loro limite è invece una forma, cioè un solo messaggio e nulla dopo. Aggiungere un EHLO a una sonda verificata a zero è ora un test rosso con dentro un numero.

Due dettagli di quel contatore si sono rivelati importanti.

Conta le scritture e non le letture. Ciò che l’altra parte offre spontaneamente non è qualcosa che abbiamo causato noi; ciò che inviamo sì. Ed è un contatore condiviso anziché un campo sul nostro tipo di flusso, perché la sonda TLS consegna il proprio socket a una libreria che poi ci scrive sopra i propri record di handshake. Senza un contatore condiviso, l’unica sonda che usa codice altrui sarebbe l’unica sonda a non essere contata, che è esattamente il punto in cui è più difficile ragionare su un budget di byte.

Quanto costa ciascuna sonda

SondaChe cosa inviamoChe cosa viene letto
Solo aperturaniente, verificato a 0 byteniente
Banner: FTP, Telnet, SMTP, MySQLniente, verificato a 0 byteun saluto di lunghezza limitata
SSHuna riga di identificazione, verificata a 32 bytela riga di identificazione del server, poi il suo elenco di algoritmi se lo offre spontaneamente
TLSun client hello, di lunghezza variabileserver hello e certificato
SMBun negotiate, di lunghezza variabilela risposta al negotiate
PostgreSQLla richiesta SSL, verificata a 8 byteun byte

Nulla segue l’apertura. Nessun secondo messaggio, nessun keepalive, nessun completamento di un handshake. Una volta che l’altra parte ha parlato, il socket si chiude.

SSH: trentadue byte, e perché non può andare oltre

Il protocollo SSH prevede che entrambe le estremità inviino per prima cosa una riga di identificazione. La nostra è una riga di trentadue byte, ed è l’unica cosa che la sonda scrive. Dopodiché legge: la riga di identificazione del server e poi, se il server ne invia uno senza che gli venga chiesto, il suo messaggio di scambio delle chiavi con dentro gli algoritmi offerti.

La parte importante è ciò che manca. La sonda non invia mai il proprio messaggio di scambio delle chiavi. Non è una politica, è un limite strutturale: senza di esso lo scambio delle chiavi non può procedere, non viene mai stabilita alcuna sessione, e non esiste quindi alcuno stato del protocollo in cui si possa inviare una richiesta di autenticazione. «Nessun tentativo di accesso» smette di essere una regola che qualcuno deve ricordare.

La riga di identificazione nomina il prodotto anziché imitare un client. Un amministratore che legge il proprio registro di autenticazione deve poter vedere chi si è connesso. Una stringa neutra o falsificata farebbe sembrare ricognizione un controllo legittimo, che è un pessimo scambio per i due secondi di oscurità che compra.

C’è un costo, e vale la pena nominarlo perché si presenta come un problema di qualità dei dati anziché come un errore. Alcuni server aspettano il messaggio di scambio delle chiavi del client prima di inviare il proprio, e il protocollo lo consente in entrambi gli ordini. Quando succede, la sonda ottiene una stringa di versione e nessun elenco di algoritmi. La copertura per quella porta è allora parziale, e deve essere registrata come parziale. Se venisse registrata come completa con un elenco vuoto, il motore di regole concluderebbe che non viene offerto alcun algoritmo obsoleto, che è il risultato peggiore possibile: un «tutto a posto» pronunciato con sicurezza e derivato da un’assenza di dati.

I protocolli che parlano per primi, e la tentazione di rispondere

FTP, Telnet, SMTP e MySQL la salutano tutti. La sonda per quelli non invia proprio nulla: connettere, leggere un numero limitato di byte, chiudere. Non è una raccolta di banner, perché non c’è modo di aprire una porta simile senza ricevere il saluto, e non è stato chiesto nulla.

La tentazione dell’EHLO è reale e abbiamo deciso di rinunciarvi. Sapere se un server di posta offre STARTTLS ha davvero valore. Richiede però l’invio di un comando di protocollo, e la regola riguardava i fatti che l’altra parte cede senza che le venga chiesto. La conseguenza è che un riscontro SMTP arriva con una gravità più bassa e una confidenza più bassa di quanto avrebbe altrimenti. È questo il prezzo onesto e deve essere visibile nel riscontro anziché essere liquidato a parole.

PostgreSQL è il caso intermedio interessante. Otto byte sono l’intero pacchetto di richiesta SSL: una lunghezza e un numero magico, che è la prima cosa che invia qualsiasi client e che non è una query, non è una credenziale e non è un accesso. Il server risponde con un solo byte che dice se parlerà TLS. Un byte di risposta basta a distinguere un database che richiede la cifratura da uno che non la richiede, e non esiste versione di questo controllo che invii di meno.

Una porta aperta non è una vulnerabilità

Che la porta 445 risponda non vuol dire che SMB1 sia abilitato. Sono due fatti separati e hanno bisogno di due campi separati.

La nostra sonda registra uno stato per la porta, cioè se c’è qualcuno, e al massimo un elemento di prova, cioè che cosa ha detto. Un negotiate che torna senza un dialetto vecchio le lascia una porta aperta e nient’altro, e quello è un risultato completo e non parziale. La tentazione di far collassare i due in un’unica gravità è forte, perché produce un cruscotto assai più emozionante.

L’igiene delle prove sta nello stesso paragrafo, perché un saluto non è una stringa innocua. I banner FTP e SMTP portano in pratica il nome host del cliente, a volte il nome host del cliente di quel cliente, e ogni tanto un percorso su disco. Conservarli grezzi vuol dire che la sua tabella dei riscontri diventa un posto in cui si accumulano informazioni personali e strutturali sulle reti di altri, conservate per tutto il tempo in cui si conservano i riscontri, visibili a chiunque possa leggerne uno.

Quindi ciò che esce dalla sonda è un token di protocollo normalizzato, un frammento di versione ritagliato secondo una regola documentata, il numero di byte letti e un hash dei byte grezzi. L’hash rende confrontabili due scansioni senza che nessuno conservi il testo. È quest’ultima proprietà quella che le manca se la salta: senza di essa, «è cambiato dal mese scorso» richiede proprio la cosa che aveva deciso di non conservare.

Il sensore non ha opinioni

Un’ultima separazione, ed è quella che rende manutenibile tutto il resto.

L’agente misura. Non risolve nomi host, non tiene un elenco di porte proprio e non sceglie i bersagli: l’indirizzo e le porte vengono scritti dalla piattaforma, che prima di inviarli ha controllato che l’indirizzo sia attualmente assegnato a quell’organizzazione e a quel server. Senza bersagli, l’agente non misura nulla.

Il giudizio avviene sulla piattaforma, a partire da un insieme di regole che si può leggere e versionare. Questo conta perché un riscontro sopravvive al codice che lo ha prodotto. Fra sei mesi qualcuno chiederà perché una scansione di settembre è stata valutata alta, e l’unico modo di rispondere è avere le regole e le misure come cose separate e recuperabili singolarmente. Se la gravità fosse cotta dentro al sensore, la risposta sarebbe «perché quella versione dell’agente la pensava così», che non è una risposta.

È lo stesso ragionamento per cui l’agente si connette verso l’esterno e non è mai in ascolto, e per cui le credenziali restano sull’host invece di viaggiare fino a noi. Un componente che riferisce soltanto ciò che ha misurato è un componente il cui raggio d’azione si può descrivere in una frase.

Che cosa metteremmo per iscritto

  1. Trasformi la promessa in un numero. Se la sua regola riguarda ciò che invia, conti ciò che invia e faccia asserzioni sul conteggio. Un commento non può fallire.
  2. Preferisca i limiti strutturali. Non inviare un messaggio di scambio delle chiavi significa che l’autenticazione è irraggiungibile, il che è più forte di una regola che dice di non autenticarsi.
  3. Lasci che una sonda sia parziale. I dati mancanti devono essere visibili come mancanti, perché l’alternativa è un negativo detto con sicurezza che nessuno può verificare.
  4. Tenga «aperto» e «vulnerabile» in campi diversi, e faccia in modo che il motore di regole li combini in un posto che si può leggere.
  5. Non conservi i banner grezzi. Normalizzi, calcoli l’hash, e conservi l’hash per il confronto.
  6. Metta l’opinione sulla piattaforma, in modo che un giudizio del trimestre scorso sia ancora spiegabile in questo.

Per chiarezza sul nostro prodotto: questo è un solo rilevatore, sta dietro a un interruttore a livello di organizzazione che al momento è spento, e non è una cosa che oggi si possa comprare. Le misure qui sopra vengono dall’implementazione e dai suoi test, non da una pagina di marketing.

Costruito per Provibr, dove l’agente gira dentro la rete del cliente. Il lato verifica dello stesso progetto, dimostrare che un nome host appartiene a chi lo chiede, è in come dimostrare di possedere un nome host.

Inizi con un host e un agente

Crei un account, installi l’agente con un solo comando e colleghi il suo primo host Proxmox. Il periodo di prova dura 14 giorni e non chiede alcun dato di pagamento.