Estrarre il Nome del Server di Indicazione (SNI) dal cliente TLS ciao

Come estrarre il Nome del Server Indicazione da parte di un Cliente TLS messaggio di saluto. Sto attualmente lottando per capire questo molto criptico RFC 3546 su Estensioni TLS, in cui la SNI è definito.

Cose che ho capito finora:

  • L’host è utf8 codificato e leggibile quando si utf8 enocde buffer.
  • C’è un byte prima che l’host, che determina la durata dell’.

Se potessi trovare l’esatta posizione di byte di lunghezza, estrarre la SNI sarebbe abbastanza semplice. Ma come faccio a byte, in primo luogo?

  • L’approccio diretto è il tentativo di prendere in è sbagliato. È necessario analizzare la richiesta, tra cui le sue estensioni e quindi di ottenere i dati dal corrispondente estensione.
  • Sì, ne sono certo, ma io in realtà non so come analizzarlo. Si capisce come l’handshake TLS funziona?
  • Certo, faccio come offriamo una libreria di protezione come uno dei nostri principali prodotti. È necessario aprire la RFC ( tools.ietf.org/html/rfc5246 ) e la sua attuazione.
  • Haha, beh, grazie, è come avere 100 pagine di pura tecnologia. Credo che le cose iniziano a farsi interessanti su pagina 41. Questo è dove le estensioni sono detto, che in cambio sono descritto in RFC 3546. Oh mio, Oh mio. 😀
  • Ehi guarda che un’altra risposta, in cui Eugene è in vendita a 20 mila dollari di prodotto come risposta. Credo che scegliendo la gente a un enorme RFC e rendendo il compito sentire prepotentemente scoraggiante è una tattica di vendite?
  • Correlate: serverfault.com/questions/574405/…
  • stackoverflow.com/questions/39624745/…

InformationsquelleAutor buschtoens | 2013-07-24

 

4 Replies
  1. 31

    Ho fatto questo sniproxy, l’esame di un cliente TLS ciao a pacchetto in Wireshark durante la lettura che la RFC è un buon modo per andare. Non è troppo difficile, solo un sacco di campi a lunghezza variabile è necessario andare oltre e di controllo verifica se hai il corretto tipo di elemento.

    Sto lavorando sul mio test di destra, e con annotazioni esempio di pacchetto che potrebbe essere di aiuto:

    const unsigned char good_data_2[] = {
        //TLS record
        0x16, //Content Type: Handshake
        0x03, 0x01, //Version: TLS 1.0
        0x00, 0x6c, //Length (use for bounds checking)
            //Handshake
            0x01, //Handshake Type: Client Hello
            0x00, 0x00, 0x68, //Length (use for bounds checking)
            0x03, 0x03, //Version: TLS 1.2
            //Random (32 bytes fixed length)
            0xb6, 0xb2, 0x6a, 0xfb, 0x55, 0x5e, 0x03, 0xd5,
            0x65, 0xa3, 0x6a, 0xf0, 0x5e, 0xa5, 0x43, 0x02,
            0x93, 0xb9, 0x59, 0xa7, 0x54, 0xc3, 0xdd, 0x78,
            0x57, 0x58, 0x34, 0xc5, 0x82, 0xfd, 0x53, 0xd1,
            0x00, //Session ID Length (skip past this much)
            0x00, 0x04, //Cipher Suites Length (skip past this much)
                0x00, 0x01, //NULL-MD5
                0x00, 0xff, //RENEGOTIATION INFO SCSV
            0x01, //Compression Methods Length (skip past this much)
                0x00, //NULL
            0x00, 0x3b, //Extensions Length (use for bounds checking)
                //Extension
                0x00, 0x00, //Extension Type: Server Name (check extension type)
                0x00, 0x0e, //Length (use for bounds checking)
                0x00, 0x0c, //Server Name Indication Length
                    0x00, //Server Name Type: host_name (check server name type)
                    0x00, 0x09, //Length (length of your data)
                    //"localhost" (data your after)
                    0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x68, 0x6f, 0x73, 0x74,
                //Extension
                0x00, 0x0d, //Extension Type: Signature Algorithms (check extension type)
                0x00, 0x20, //Length (skip past since this is the wrong extension)
                //Data
                0x00, 0x1e, 0x06, 0x01, 0x06, 0x02, 0x06, 0x03,
                0x05, 0x01, 0x05, 0x02, 0x05, 0x03, 0x04, 0x01,
                0x04, 0x02, 0x04, 0x03, 0x03, 0x01, 0x03, 0x02,
                0x03, 0x03, 0x02, 0x01, 0x02, 0x02, 0x02, 0x03,
                //Extension
                0x00, 0x0f, //Extension Type: Heart Beat (check extension type)
                0x00, 0x01, //Length (skip past since this is the wrong extension)
                0x01 //Mode: Peer allows to send requests
    };
    
    • Fresco, grazie per la condivisione di questo. +1
    • Questo è chiaramente più elaborato rispetto al mio originale half-assed risposta. Hanno un segno di spunta. 😀
    • Grande, io sono venuto qui perché volevo avere un non-decifrare semplice TLS server di inoltro basato sul SNI. Quindi, con sniproxy che è già stato fatto.
  2. 6

    Utilizzare WireShark e di acquisire solo TLS (SSL) pacchetti con l’aggiunta di un filtro tcp port 443. Quindi trovare un Client “Ciao” Messaggio. È possibile vedere i dati grezzi di seguito.

    Espandere Secure Socket Layer->TLSv1.2 Record Layer: Handshake Protocol: Client Hello->...

    e vedrete Extension: server_name->Server Name Indication extension. Il nome del server nella stretta di mano pacchetto non è crittografato.

    http://i.stack.imgur.com/qt0gu.png

    • Stiamo cercando un modo programmatico per determinare la SNI. Tuttavia, questo potrebbe essere interessante per alcune persone, tuttavia, quindi vi prego di non cancellare questo.
  3. 1

    Ho notato che il dominio è sempre anteporre da due zero byte e un byte di lunghezza. Forse è 24 bit unsigned integer, ma io non posso testare, visto che il mio server DNS non consentire nomi di dominio oltre 77 caratteri.

    Momento che la conoscenza mi si avvicinò con questa (Node.js) codice.

    function getSNI(buf) {
      var sni = null
        , regex = /^(?:[a-z0-9-]+\.)+[a-z]+$/i;
      for(var b = 0, prev, start, end, str; b < buf.length; b++) {
        if(prev === 0 && buf[b] === 0) {
          start = b + 2;
          end   = start + buf[b + 1];
          if(start < end && end < buf.length) {
            str = buf.toString("utf8", start, end);
            if(regex.test(str)) {
              sni = str;
              continue;
            }
          }
        }
        prev = buf[b];
      }
      return sni;
    }
    

    Questo codice è una sequenza di due a zero byte. Se ne trova uno, si assume i seguenti byte è un parametro di lunghezza. Controlla se la lunghezza è ancora nel limite del buffer e se così recita la sequenza di byte UTF-8. Poi, uno potrebbe RegEx la matrice e l’estratto di dominio.

    Funziona incredibilmente bene! Comunque, ho notato qualcosa di strano.

    '�\n�\u0014\u0000�\u0000�\u00009\u00008�\u000f�\u0005\u0000�\u00005�\u0007�\t�\u0011�\u0013\u0000E\u0000D\u0000f\u00003\u00002�\f�\u000e�\u0002�\u0004\u0000�\u0000A\u0000\u0005\u0000\u0004\u0000/�\b�\u0012\u0000\u0016\u0000\u0013�\r�\u0003��\u0000\n'
    '\u0000\u0015\u0000\u0000\u0012test.cubixcraft.de'
    'test.cubixcraft.de'
    '\u0000\b\u0000\u0006\u0000\u0017\u0000\u0018\u0000\u0019'
    '\u0000\u0005\u0001\u0000\u0000'
    

    Sempre, non importa che cosa il sottodominio ho scelto, il dominio è mirata due volte. Sembra che la SNI campo è nidificato all’interno di un altro campo.

    Sono aperto a suggerimenti e miglioramenti! 🙂

    Ho trasformato questo in un Nodo modulo, per tutti, chi se ne frega: sni.

    • Motivo per downvote?
    • Non credo che le espressioni regolari sono il modo migliore per estrarre i dati da un file binario protocollo di crittografia. Il Client Hello messaggio include 32 byte di dati casuali che potrebbero corrispondere espressione.
    • Non so che merita un downvote, voglio dire, ha trovato una soluzione. Ho incontrato lo stesso, ma come dlundquist note, non ho intenzione di fare affidamento su di essa, essere coerenti o escludere la possibilità di byte casuali inquinanti per l’espressione regolare partita. Tuttavia, il lavoro.
  4. 1

    Per chiunque fosse interessato, questa è una versione provvisoria del codice C/C++. Ha lavorato finora. La funzione restituisce la posizione del nome del server in una matrice di byte contenente il Client Hello e la lunghezza del nome del len parametro.

    char *get_TLS_SNI(unsigned char *bytes, int* len)
    {
        unsigned char *curr;
        unsigned char sidlen = bytes[43];
        curr = bytes + 1 + 43 + sidlen;
        unsigned short cslen = ntohs(*(unsigned short*)curr);
        curr += 2 + cslen;
        unsigned char cmplen = *curr;
        curr += 1 + cmplen;
        unsigned char *maxchar = curr + 2 + ntohs(*(unsigned short*)curr);
        curr += 2;
        unsigned short ext_type = 1;
        unsigned short ext_len;
        while(curr < maxchar && ext_type != 0)
        {
            ext_type = ntohs(*(unsigned short*)curr);
            curr += 2;
            ext_len = ntohs(*(unsigned short*)curr);
            curr += 2;
            if(ext_type == 0)
            {
                curr += 3;
                unsigned short namelen = ntohs(*(unsigned short*)curr);
                curr += 2;
                *len = namelen;
                return (char*)curr;
            }
            else curr += ext_len;
        }
        if (curr != maxchar) throw std::exception("incomplete SSL Client Hello");
        return NULL; //SNI was not present
    }
    

Lascia un commento