Alfonso Maruccia

Ticketbleed, un nuovo caso Heartbleed?

Individuata una nuova vulnerabilitÓ telematica potenzialmente pericolosa, e giÓ c'Ŕ chi fa paragoni con la famigerata falla nota come Heartbleed. Forse le implicazioni sono le stesse, ma il livello di pericolositÓ Ŕ molto diverso

Roma - Il ricercatore Filippo Valsorda, attualmente impiegato presso il "Crypto Team" di Cloudflare, ha scovato un bug di sicurezza nelle appliance di rete ribattezzato Ticketbleed. La falla (CVE-2016-9244) potrebbe mettere a rischio le connessioni sicure (HTTPS) di un migliaio tra i siti Web più popolari al mondo, e al momento non esistono patch correttive ma solo misure tampone per mitigare il problema.

Ticketbleed è una vulnerabilità software nell'implementazione proprietaria del protocollo TLS/SSL di F5 Networks, società ben nota per i suoi apparati di rete venduti sotto il marchio F5 BIG-IP e utilizzati da alcuni colossi del Web come firewall e bilanciatori di traffico. F5 ha ammesso l'esistenza del problema, pubblicando un elenco di prodotti vulnerabili e le azioni da compiere per mitigare gli effetti del bug.

La nuova vulnerabilità agisce su una caratteristica propria del protocollo TLS di F5 nota come "ticket di sessione", funzionalità progettata per velocizzare le transazioni Web protette con il riutilizzo delle chiavi crittografiche precedentemente usate da una connessione HTTPS già stabilita. Inviando una richiesta appositamente malformata, un malintenzionato ha possibilità di esporre 31 byte di memoria non utilizzata per ogni singolo tentativo.
Inviando una serie di richieste successive, questo l'effetto potenzialmente molto grave della falla, si potrebbero identificare informazioni sensibili o comunque private fino a compromettere la sicurezza della HTTPS. Valsorda dice di essersi limitato a stanare i siti vulnerabili, ma di non essere particolarmente esperto dell'analisi di memoria a basso livello.

Gli effetti di Ticketbleed - un "buffer overread" con esposizione di dati in memoria - fanno pensare all'utilizzo, nella realizzazione del protocollo TLS di F5, di un linguaggio di programmazione non protetto contro questo genere di bug come il C, spiega Valsorda. Per quanto non facile né immediata, la conversione del codice a qualcosa di più moderno come Rust o Go avrebbe evitato il problema.

Al momento Ticketbleed è un baco per cui non esiste patch, ma che può essere eliminato con la disabilitazione dei ticket di sessione da parte degli amministratori di rete. E anche se non pochi commentatori hanno fatto un parallelo con Heartbleed i livelli di rischio appaiono molto diversi.

La storica falla "sistemica" di Internet, emersa dai pozzi del codice di OpenSSL nel 2014, era in grado di esporre una quantità di informazioni molto superiore ai 31 byte di Ticketbleed (64 Kilobyte) e coinvolgeva una libreria open source usata dalla stragrande maggioranza degli operatori di rete (OpenSSL appunto) contro l'implementazione proprietaria di F5. Ticketbleed non è insomma un nuovo baco Heartbleed, anche se gli assomiglia non poco.

Alfonso Maruccia
Notizie collegate
24 Commenti alla Notizia Ticketbleed, un nuovo caso Heartbleed?
Ordina
  • Per heartbleed le maggiori distro ed OS open avevan le patch rapidamente pronte, le vittime sono state (e magari sono ancora) solo gente che non manuteneva i suoi servizi o che non poteva farlo per i pezzi di ferro cuciti al sw di cert'uni produttori (router, telefoni &c basati su codice open, anche reso disponibile nel rispetto delle licenze ma di fatto abbandonati). Per questo baco bisogna aspettare che F5 si svegli e non vi sono certezze sul quando e come lo farà.
    non+autenticato
  • E' closed sorcio corporate se la patch non c'è vuol dire che non serve. Fidati!
    non+autenticato
  • Però in ogni contratto corporate c'è scritto che il software è "concesso in uso" senza alcuna garanzia di sorta... Hum, il sola-ometro in genere va fuori scala...

    Ps tempo fa un collega disse che il software "enterprise" assomigliava alla nave Enterprise di Star Trek: c'è sempre qualche disastro critico che rischia di far saltar tutto, non c'è mai alcun "backup" di nessun sistema critico (c'è 1 solo computer, che fa tutto, un solo reattore per tutto ecc)... 'Somma qualcosa che farebbe sbatter fuori a calci qualsiasi matricola di ingegneria!
    non+autenticato
  • Sacrosante parole quelle di ben10, se non conoscete niente di assembly forse è il caso, magari, di non fare considerazioni in modo da non risultare saccenti ma ottusi.
  • - Scritto da: darkinvader
    > Sacrosante parole quelle di ben10, se non
    > conoscete niente di assembly forse è il caso,
    > magari, di non fare considerazioni in modo da non
    > risultare saccenti ma
    > ottusi.

    Ma certo. Solo un incerto avrebbe partorito dette incertezze! Rotola dal ridere
    non+autenticato
  • Guarda che vale anche il contrario. Dire che bisogna affidarsi a C++ / C / asm per forza di cose ed affidarsi alla "bontà" del programmatore è di una pttusità ben più elevata di voler fare tutto con Java (o mettici il linguaggio che vuoi).

    Ricordo inoltre che non è detto che chi conosce il C++ poi sappia anche creare codice utile ad esempio per frangenti differenti dall'informatica "pura".

    Fonte: programmo in asm, C, C++ probabilmente da prima di molti di voi.
  • Rust e Go? Sciacquatevi la bocca. Forse si può passare al C++, ma di sicuro non a linguaggi in cui è _obbligatorio_ l'uso del garbage collector. Altrimenti vi potete scordare le prestazioni di rete a cui siamo abituati.
    non+autenticato
  • non sai di cosa parli, informati
    non+autenticato
  • Gli unici linguaggi preformanti sono quelli che producono eseguibili in opcode che vengono eseguiti nativamente dalla CPU (niente bytecode o layer VM o interpreti) e dove la memoria è gestita senza astrazioni (niente garbage collector, niente virtualizzazioni dei puntatori ecc).
    Sostanzialmente solo l'utilizzo dell'assembly della CPU specifica (da parte di chi lo conosce e ne è capace) puo' generare il codice ottimale. Il linguaggio C puo' avvicinarsi molto allo stesso risultato, ma soffrendo di una ABI interna per dialogare con le librerie (come le implementazioni delle libc, per lo meno per delle convenzioni sulle chiamate alle funzioni, allineamento dello stack ecc) è costretto a fare qualche sacrificio sulle performance. Il linguaggio C++ se usato con classi con funzioni virtual, la RTTI ecc, soffre tantissimo sulle performance. Java, python, rust ma anche go sono da buttare nel cesso perché per questi argomenti non sono nemmeno da considerare.
    L'uso del C, con qualche funzione minore in assembly, per codice che deve lavorare senza perdite di tempo è l'unica strada e senza alternative. Il bound-checking viene effettuato costantemente su tutto nei linguaggi per incompetenti, mentre nel C non viene mai eseguito automaticamente. Quindi è il programmatore che quando serve devi ricordarsi di farlo manualmente. non è colpa del linguaggio di programmazione se il programmatore sbaglia. Invece è colpa del linguaggio di programmazione se quello fa dei bound-checking di continuo anche quando non serve.
    Gli sparagestionali winari non possono arrivare né a comprendere né a concepire delle differenze di performance (nel loro ambiente è anche vero che non è rilevante) tra il codice generato dai vari linguaggi quindi è inutile discuterne con loro.
    Più il programmatore è competente, più riesce a utilizzare linguaggi di basso livello e più riesce specializzare e a sfruttare la conoscenza dell'hardware per incrementare le performance del suo programma. Per codec audio/video, applicazioni di rete (es: router), microcontrollori/dispositivi embedded con hardware limitato la differenza è importante e si nota. Altrove dove va bene anche il visual basic, e le performance non contano niente, allora arrivano i winari e gli scripettari a predicare che puoi fare tutto in C#, java, shell script.... ma la realtà è diversa.
    non+autenticato
  • > Più il programmatore è competente, più riesce a
    > utilizzare linguaggi di basso livello e più
    > riesce specializzare e a sfruttare la conoscenza
    > dell'hardware per incrementare le performance del
    > suo programma. Per codec audio/video,
    > applicazioni di rete (es: router),
    > microcontrollori/dispositivi embedded con
    > hardware limitato la differenza è importante e si
    > nota. Altrove dove va bene anche il visual basic,
    > e le performance non contano niente, allora
    > arrivano i winari e gli scripettari a predicare
    > che puoi fare tutto in C#, java, shell script....
    > ma la realtà è
    > diversa.

    E pensare che la prima cosa che mi hanno insegnato si basa su tre parole:

    Astrazione, Riduzione, Induzione
    non+autenticato
  • sole cuore amore
    non+autenticato
  • - Scritto da: dammi tre parole
    > sole cuore amore

    E la zappa per dissodare i campi
    non+autenticato
  • Bé la realtà è anche un pelino diversa da quella che descrivi te mettendo insieme Rust già solo con Go, per di più poi unito a Python e Java...

    Per quanto riguarda l'asm ti faccio notare che a sua volta è compilato da un assembler che non è scritto da nessuna parte ne sappia di più di un compilatore C o Rust sulle istruzioni della CPU per cui compila. A parte ciò se compilassi veramente usando tutte le possibili ottimizzazioni della CPU che hai ti troveresti ad aver binari praticamente importabili, un hello world compilato per un Intel Core i3 di VI generazione avrebbe ottime probabilità di non girare su un Core i7 di V genrazione ecc. In pratica dovresti compilare (e ciò in se non sarebbe male, ammazzerebbe definitivamente il sw proprietario) per ogni CPU esistente e ricompilare tutto quando cambi CPU sullo stesso ferro inoltre chi scrive tanto i compilatori quanto gli assembler avrebbe un lavoro a dir poco improbo per stare al passo col ferro.
    non+autenticato
  • > Per quanto riguarda l'asm ti faccio notare che a
    > sua volta è compilato da un assembler che non è
    > scritto da nessuna parte ne sappia di più di un
    > compilatore C o Rust sulle istruzioni della CPU
    > per cui compila.

    Ti stai sbagliando di grosso.
    Un assembler è in grado di tradurre TUTTO il set di istruzioni per la CPU per cui è stato concepito.
    Avendo il codice sorgente in assembly come input (un tempo conosciuto anche come il "listato") l'assembler si limita a tradurre istruzione-per-istruzione in opcode. L'unica modifica ben nota che esegue un assember sul suo codice sorgente in ingresso è quella di calcolare gli indirizzi per i jump (che a seconda dell'assembler usato possono generare istruzioni di lunghezza diverse....quindi può essere importante). Per il resto puoi anche inserire istruzioni inesistenti scrivendo ".byte 123,123" e l'assembler semplicemente trascriverà quei valori. Puoi compilare un sorgente che usa istruzioni AVX2 sia col loro valore numerico (.byte...) sia scrivendo il nome mnemonico e l'assembler genererà l'opcode appropriato. Se andrai a eseguire il programma su un i386 che di sicuro non conosce le istruzioni AVX2 allora non funzionerà (la CPU genererà un'eccezione di invalid opcode o una #GP). In realtà pure questo può essere più complicato di così. Per esempio l'istruzione PAUSE del Pentium4 genera come opcode F3 90 (due byte). Se ci fai caso, l'pcode "F3" è lo stesso opcode del prefisso di istruzione "REP", mentre l'istruzione "90" è l'opcode del NOP. Per una coincidenza eseguire l'istruzione PAUSE su una CPU precedente il Pentium4 non genera nessuna eccezione perché viene "scambiata" per un'altra istruzione valida ma che non ha nessun effetto "REP;NOP". E' un trucco ben noto.
    http://x86.renejeschke.de/html/file_module_x86_id_... PAUSE
    http://x86.renejeschke.de/html/file_module_x86_id_... REP
    http://x86.renejeschke.de/html/file_module_x86_id_... NOP

    >A parte ciò se compilassi
    > veramente usando tutte le possibili
    > ottimizzazioni della CPU che hai ti troveresti ad
    > aver binari praticamente importabili, un hello
    > world compilato per un Intel Core i3 di VI
    > generazione avrebbe ottime probabilità di non
    > girare su un Core i7 di V genrazione ecc.

    Infatti per i programmi scritti in C si usa l'apposito switch nel compilatore... o non si usa con lo scopo di generare eseguibili "generici" che funzionino ovunque e che pero' non sono ottimizzati (o cambiando punto di vista: che sono di fatto ottimizzati per la CPU più antica).
    Preprocessore del compilatore -> compilatore (fa le ottimizzazioni e genera il listato) -> assembler (genera file oggetto) -> linker (genera l'eseguibile, se usa l'LTO può fare l'inlining di alcune funzioni ma meglio evitare per ora)
    Nella sequenza quindi, basta che per la fase del compilatore usi l'apposito switch, che per il GCC è -march.
    Lo trovi proprio qui come primo nella lista:
    https://gcc.gnu.org/onlinedocs/gcc/x86-Options.htm...
    Certamente (come hai detto) se compili per pentium4 poi l'eseguibile non andrà su un pentium3 e forse nemmeno su CPU successive (troppo lungo da spiegare... ma per esempio il codice non sta' a guardare il CPUID quindi se una feature sparisce, il programma non se ne accorge)

    > In
    > pratica dovresti compilare (e ciò in se non
    > sarebbe male, ammazzerebbe definitivamente il sw
    > proprietario) per ogni CPU esistente e
    > ricompilare tutto quando cambi CPU sullo stesso
    > ferro

    Basta che scarichi il sorgente di GMP per capire: https://gmplib.org/
    Alternativamente puoi scaricare anche il sorgente della glibc. Dopo averli letti capirai....... tutte quelle funzioni importanti, delle librerie, che vengono usate spesso e che sono rilevanti per le performance sono scritte in assembly per ogni CPU. C'è la stessa funzione scritta in assembly (in alcuni casi con le intrinsic, solitamente in assembly puro) con istruzioni SSE2, con l'SSE4.1, con l'AVX2, con l'AVX512..... e in assembly "generico" per i686 e AMD64(cioè SSE2 con 16 registri) nel caso usi un PC un po' vecchiotto. Anche per ARM64, mips, mipsel ecc... succede la stessa cosa.

    > inoltre chi scrive tanto i compilatori
    > quanto gli assembler avrebbe un lavoro a dir poco
    > improbo per stare al passo col
    > ferro.

    Non è così impervio come sembra ed accade proprio quello che dici: chi scrivi i compilatori (per gli assembler è una cavolata) si tiene al passo coi tempi e aggiunge al suo compilatore nuovi casi in cui vengono generate istruzioni diverse a seconda della CPU target (nonostante il codice in C in input sia lo stesso il listato generato è diverso).
    Semmai il problema è che risulta molto più facile a chi scrive il programma usare direttamente il codice in assembly giusto, che sperare che il compilatore se ne accorga. Per la vettorizzazione delle istruzioni nei cicli for/while il GCC fa miracoli, ma alcuni trucchi che potrebbe fare gli sono invisibili. Quindi se usi tu direttamente le istruzioni SIMD è meglio (ma la stessa logica vale anche per le altre unità... ALU, AGU, x87/MMX..). Per esempio non puoi mai sapere che cosa succede se scrivi "long double n" (in C in realtà col GCC lo puoi fare, ma solo se lo costringi ad usare una sola unità a virgola mobile... nella pagina di prima guarda anche lo switch -mfpmath.... ) se scrivi in assembly (anche se solo un inline assembly in un sorgente in C) puoi scrive manualmente le istruzioni per l'x87 (il coprocessore matematico dell'x86) in modo da sfruttare le operazioni a virgola mobile a 80-bit, mentre se lasci decidere al compilatore quello genererà le istruzioni a virgola mobile per l'SSE2 (e successivi) a 64-bit (l'SSE manovra solo i float a 32-bit, quindi su CPU a 32 bit compreso il P4 sarà invece usato l'x87, ma l'AMD64 usa esclusivamente l'SSE2...perdendo precisione, ameno che non obblighi l'esecuzione dall'x87).

    Mentre un assembler conosce TUTTE le istruzioni per la CPU che supporta, un compilatore no (e non scriverà nel listato le istruzioni che non conosce). Quindi non sempre genererà codice ottimale. Inoltre i compilatori per il C non generano mai le istruzioni nth. E quindi le funzioni come memcpy per grandi aree di memoria si possono scrivere (ottimizzate) solo in assembly perché il compilatore non puo' usare quelle istruzioni (che sono per esempio MOVNTDQ, MOVNTI, MOVNTPD, MOVNTPS, MOVNTQ, MASKMOVDQU e svariate altre) perché non sà gestirle e sarebbe svantaggioso se imponesse delle fence sulla pipeline di store o load di sua iniziativa. Solo chi programma in assembly sà quando usarle (ad esempio per fare una memcpy o memset, ma solo quando viene messa in esecuzione su grandi aree di memoria; ma dipende anche dalla dimensione della cache della CPU; e da fattori temporali quando si accederà nuovamente a quella memoria?) Ho finito i caratteriSorride Gli altri linguaggi cmq sono una caxata.
    non+autenticato
  • > Ti stai sbagliando di grosso.
    > Un assembler è in grado di tradurre TUTTO il set
    > di istruzioni per la CPU per cui è stato
    > concepito.
    Ceeerrto, perché secondo gli assemblers esistenti sono fatti e mantenuti dai produttori di CPU che aggiornano la grammatica per ogni singola istruzione che aggiungono vero?

    > Avendo il codice sorgente in assembly come input
    > (un tempo conosciuto anche come il "listato")

    > Nella sequenza quindi, basta che per la fase del
    > compilatore usi l'apposito switch, che per il GCC
    > è
    > -march.
    -march vuol dire machine architetture, non sono istruzioni specifiche per una singola CPU, non c'è alcuna differenza tra il binario che genera un backend di gcc e un assembler, la differenza è che l'assembly scritto a mano, in teoria, contiene solo quel che serve senza le aggiunte che il linguaggio impiega per i suoi automatismi interni. Giacché ti piace uesto
    argomento qui hai un confronto d'esempio con listati e dump
    timelessname.com/elfbin/

    > Non è così impervio come sembra ed accade proprio
    > quello che dici: chi scrivi i compilatori (per
    > gli assembler è una cavolata) si tiene al passo
    > coi tempi e aggiunge al suo compilatore nuovi
    > casi in cui vengono generate istruzioni diverse a
    > seconda della CPU target (nonostante il codice in
    > C in input sia lo stesso il listato generato è
    > diverso).
    Listato? Forse hai incollato male...

    > Mentre un assembler conosce TUTTE le istruzioni
    > per la CPU che supporta, un compilatore no (e non
    > scriverà nel listato le istruzioni che non
    > conosce). Quindi non sempre genererà codice
    > ottimale.
    Ahem, guarda che i compilatori sono normalmente composti da un frontend che conosce il linguaggio da compilare ed un backend che conosce il ferro
    per cui compilarlo... Ma, per curiosità, ti interessa veramente quel che dici o fai collages di copia&incolla da articoli sparsi per l'web?
    non+autenticato
  • - Scritto da: xte


    > scritto da nessuna parte ne sappia di più di un
    > compilatore C o Rust sulle istruzioni della CPU

    di sicuro ne sa meno sulla struttura astratta del programma che stiamo realizzando, il che significa che e' impossibilitato a fare qualsiasi tipo di controllo sulla correttezza delle operazioni svolte dal programma

    sull'ottimizzazione, boh, loro dicono che il programmatore e' sempre meglio del compilatore, ma ho qualche dubbio in proposito

    > per cui compila. A parte ciò se compilassi
    > veramente usando tutte le possibili
    > ottimizzazioni della CPU che hai ti troveresti ad
    > aver binari praticamente importabili, un hello
    > world compilato per un Intel Core i3 di VI
    > generazione avrebbe ottime probabilità di non
    > girare su un Core i7 di V genrazione ecc. In
    > pratica dovresti compilare (e ciò in se non
    > sarebbe male, ammazzerebbe definitivamente il sw
    > proprietario) per ogni CPU esistente e
    > ricompilare tutto quando cambi CPU sullo stesso
    > ferro inoltre chi scrive tanto i compilatori
    > quanto gli assembler avrebbe un lavoro a dir poco
    > improbo per stare al passo col
    > ferro.

    fino ad oggi gli unici che possono farlo in termini realistici, sono i compilatori jit

    ma ripeto, qui il problema e' la correttezza dei programmi, per cui assembly non puo' proprio avervi cittadinanza
    non+autenticato
  • > che puoi fare tutto in C#, java, shell script....
    > ma la realtà è
    > diversa.

    Vero, la realtà è che i *grandi programmatori* che usano C e assembly hanno riempito i router di bug... e ogni tanto saltano fuori.
    non+autenticato