Décrypter du SSL

 druide

Handshake

L'établissement d'une connexion sécurisée SSL se fait en 9 étapes.



  1. Le client envoie le message "Client Hello". Ce message contient la version de SSL utilisé, la liste des méthodes de chiffrement supportées par le client ainsi qu'une chaîne de caractère correspondant à un nombre aléatoire. Ce nombre aléatoire sera utilisé pour crée la « master key » de chiffrement par la suite. Le message contient également le mode de compression choisit mais actuellement, SSLv3 ne propose aucun compression.



  2. Le serveur SSL répond avec le message "Server Hello". Ce message contient la suite de chiffrement choisie parmi celles transmises par le client, un identifiant de session et aussi une chaîne de caractère correspondant à un nombre aléatoire. Ce nombre aléatoire sera lui aussi utilisé pour crée la « master key » de chiffrement par la suite. Le serveur transmet également son certificat, et donc, la clé publique de son chiffrement asymétrique.






  3. Le client vérifie l'authenticité du certificat, pour cela, il utilise la signature présente dans le certificat, l'autorité de certification CA, la validité (date de début et date de fin) ainsi que la révocation (le certificat a-t-il été révoqué).




  4. Le client envoie au serveur la clé « master key » qu'il a calculé à l'aide des nombres aléatoires échangés précédemment ainsi que d'autre valeur. Cette clé ne circule pas en claire, bien sûr! Elle a été chiffrée avec la clé publique du serveur reçue au point précédent.




  5. [OPTIONNELLE] Si le serveur a également requis un certificat de la part du client, ce dernier le lui envoie à cette étape...
  6. [OPTIONNELLE] Le serveur vérifie la validité du certificat provenant du client.
  7. Le client envoie le message "finished" qui est déjà lui-même chiffré avec la « master key » échangé précédemment. Le client indique ainsi au serveur qu'il a terminé sa part du « handshake ». A partir d'ici, tout ce qu'on peut voir dans l'échange entre le client et le serveur est chiffré et se résume donc à...




  8. Le serveur envoie à son tour le message "finished" au client. Ce message est lui aussi chiffré.
  9. Pour le reste de la communication entre le client et le serveur, les trames échangées comportent des données chiffrées avec la clé symétrique « master key ».



Déchiffrement


Mon but est de pouvoir déchiffré un échange d'information qui s'effectuait à mon insu entre mon NAS WD my Cloud et le site de Western Digital. Je ne pense pas que ce soit un véritable secret d'état qui est échangé entre eux mais ma curiosité a quand même été piquée ☺

Comme mon NAS utilise


                  _   _ ____  _
              ___| | | |  _ \| |
             / __| | | | |_) | |
            | (__| |_| |  _ 


Compilée avec SSL provenant d'OpenSSL


# curl -V    
curl 7.32.0 (arm-unknown-linux-gnueabihf) libcurl/7.32.0 OpenSSL/1.0.1g zlib/1.2.7 libidn/1.25 libssh2/1.4.3 librtmp/2.3
Protocols: dict file ftp ftps gopher http https imap imaps ldap ldaps pop3 pop3s rtmp rtsp scp sftp smtp smtps telnet tftp 
Features: AsynchDNS GSS-Negotiate IDN IPv6 Largefile NTLM NTLM_WB SSL libz TLS-SRP 


Je me suis plongé dans le code de cURL et d'OpenSSL pour réussir à extraire la « master key » dans un échange en live. Il existe d'autre solution présentée sur le Web comme par exemple: « extraire la « master key » de la RAM en se basant sur l'emplacement mémoire de la session SSL dans la struct » mais voilà, j'ai choisi un chemin différent et c'est MON chemin ☺

De plus, je suis tombé sur un article fort intéressent dans lequel il(s) explique(nt) que Firefox et chrome peuvent exporter automatiquement la « master key » dans un fichier qui peut, à son tour, nourrir Wireshark pour lui permettre de déchiffrer un échange SSL.


Il ne me reste donc plus qu'à faire les modifications dans le code pour exporter la « master key » dans un fichier au même format que Firefox et Wireshark fera le reste.

Code source


Après avoir téléchargé le code source de cURL, j'ai cherché le "bon endroit" pour placer le code nécessaire à l'extraction des informations. En effet, comme je pouvais m'y attendre, cURL n'est pas contenu dans un seul fichier ☺.

Mon choix c'est porté sur le fichier openssl.c présent dans le répertoire curl/lib/vtls


À la ligne 2010 on trouve le commentaire suivant

/* we have been connected fine, we're not waiting for anything else. */

connssl->connecting_state = ssl_connect_3;


J'ai donc placé mon code juste après.

/*
* Export the key to decrypt the data like Firefox

* connssl->handle is a pointer to struct ssl_st defined in ssl.h

*/

if(connssl->handle->version == SSL3_VERSION)

{
char path[] = "/tmp/key";
int i = 0;
FILE *fkey = fopen(path, "a+");
if(fkey != NULL)
{
fprintf(fkey, "CLIENT_RANDOM ");

for (i=0; i { // s3 is an struct ssl3_state_st defined in ssl3.h. This struct contain unsigned char client_random[SSL3_RANDOM_SIZE];

fprintf(fkey, "%02x", connssl->handle->s3->client_random[i]);

}
fprintf(fkey, " ");

for (i=0; i { // session is an SSL_SESSION *session defined in ssl.h. It contain the master_key[SSL3_MASTER_SECRET_SIZE];

fprintf(fkey, "%02x", connssl->handle->session->master_key[i]);

}
fprintf(fkey, "\n");
fclose(fkey);
}
}
/* END export */

Explication Le code crée un fichier key dans /tmp en mode "ajout/création". Si le fichier est bien crée et qu'on a réussi à l'ouvrir, on y écrit le texte CLIENT_RANDOM . On écrit ensuite en hexadécimal les 32 octets correspondant au nombre aléatoire généré par le client lors de l'handshake (point 1), on ajoute un espace et on écrit, également en hexadécimal, les 48 octets de la fameuse « master key ».

On ajoute un retour à la ligne et on ferme le fichier.

On obtient ainsi le même format de fichier que celui de firefox.


CLIENT_RANDOM 91822d5858607ffb51064a9f50fb6e3dfe7e571c3fe0b39e4dc196d998d7b3df 47ce41f559bd0c34094e4c7657c6061054238f3182d9fb667da0f8f1860343cf8dec592bae470365b242e7e89f2d6b21
Reste plus qu'à recompiler cURL et tester. Pour recompiler, il faut suivre les indications présentes dans le fichier GIT-INFO du répertoire cURL. Soit en résumé:

ubuntu@develop:~/curl$ ./buildconf
buildconf: autoconf version 2.69 (ok)
buildconf: autom4te version 2.69 (ok)
buildconf: autoheader version 2.69 (ok)
buildconf: automake version 1.14.1 (ok)
buildconf: aclocal version 1.14.1 (ok)
buildconf: libtoolize version 2.4.2 (ok)
buildconf: GNU m4 version 1.4.17 (ok)
buildconf: running libtoolize
buildconf: converting all mv to mv -f in local m4/libtool.m4
buildconf: running aclocal
buildconf: converting all mv to mv -f in local aclocal.m4
buildconf: running autoheader
buildconf: running autoconf
buildconf: running automake
configure.ac:128: installing './compile'
configure.ac:180: installing './config.guess'
configure.ac:180: installing './config.sub'
src/Makefile.inc:112: warning: variable 'curl_SOURCES' is defined but no program or
src/Makefile.inc:112: library has 'curl' as canonical name (possible typo)
Makefile.am:139:   'src/Makefile.inc' included from here
docs/examples/Makefile.am: installing './depcomp'
buildconf: OK

ubuntu@develop:~/curl$ ./configure
  curl version:     7.39.1-DEV
  Host setup:       x86_64-unknown-linux-gnu
  Install prefix:   /usr/local
  Compiler:         gcc
  SSL support:      enabled (OpenSSL)
  SSH support:      no      (--with-libssh2)
  zlib support:     enabled
  GSS-API support:  no      (--with-gssapi)
  TLS-SRP support:  enabled
  resolver:         default (--enable-ares / --enable-threaded-resolver)
  ipv6 support:     enabled
  IDN support:      no      (--with-{libidn,winidn})
  Build libcurl:    Shared=yes, Static=yes
  Built-in manual:  enabled
  --libcurl option: enabled (--disable-libcurl-option)
  Verbose errors:   enabled (--disable-verbose)
  SSPI support:     no      (--enable-sspi)
  ca cert bundle:   /etc/ssl/certs/ca-certificates.crt
  ca cert path:     no
  LDAP support:     no      (--enable-ldap / --with-ldap-lib / --with-lber-lib)
  LDAPS support:    no      (--enable-ldaps)
  RTSP support:     enabled
  RTMP support:     no      (--with-librtmp)
  metalink support: no      (--with-libmetalink)
  HTTP2 support:    disabled (--with-nghttp2)
  Protocols:        DICT FILE FTP FTPS GOPHER HTTP HTTPS IMAP IMAPS POP3 POP3S RTSP SMTP SMTPS TELNET TFTP

ubuntu@develop:~/curl$ make
make[2]: Leaving directory '/home/ubuntu/curl/src'
make[1]: Leaving directory '/home/ubuntu/curl/src'
Making all in include
make[1]: Entering directory '/home/ubuntu/curl/include'
Making all in curl
make[2]: Entering directory '/home/ubuntu/curl/include/curl'
make  all-am
make[3]: Entering directory '/home/ubuntu/curl/include/curl'
make[3]: Leaving directory '/home/ubuntu/curl/include/curl'
make[2]: Leaving directory '/home/ubuntu/curl/include/curl'
make[2]: Entering directory '/home/ubuntu/curl/include'
make[2]: Nothing to be done for 'all-am'.
make[2]: Leaving directory '/home/ubuntu/curl/include'
make[1]: Leaving directory '/home/ubuntu/curl/include'
make[1]: Entering directory '/home/ubuntu/curl'
make[1]: Nothing to be done for 'all-am'.
make[1]: Leaving directory '/home/ubuntu/curl'

ubuntu@develop:~/curl$ sudo make install
make[7]: Leaving directory '/home/ubuntu/curl/docs/libcurl'
make[6]: Leaving directory '/home/ubuntu/curl/docs/libcurl'
make[5]: Leaving directory '/home/ubuntu/curl/docs/libcurl'
make[5]: Entering directory '/home/ubuntu/curl/docs'
make[6]: Entering directory '/home/ubuntu/curl/docs'
make[6]: Nothing to be done for 'install-exec-am'.
 /bin/mkdir -p '/usr/local/share/man/man1'
 /usr/bin/install -c -m 644 curl.1 curl-config.1 '/usr/local/share/man/man1'
make[6]: Leaving directory '/home/ubuntu/curl/docs'
make[5]: Leaving directory '/home/ubuntu/curl/docs'
make[4]: Leaving directory '/home/ubuntu/curl/docs'
make[3]: Leaving directory '/home/ubuntu/curl'
make[2]: Leaving directory '/home/ubuntu/curl'
make[1]: Leaving directory '/home/ubuntu/curl'


Et voilà. Maintenant, il faut sniffer le réseau et faire une requête cURL sur un site sécurisé en même temps.



ubuntu@develop:~$ curl -4 --sslv3 -m 5 https://www.wd2go.com/api/1.0/rest/relay_server?format=openvpn -o /tmp/relayServers >/dev/null 2>/dev/null

# en même temps depuis un autre shell

ubuntu@develop:~$ sudo tcpdump -w capture.log
tcpdump: listening on eth0, link-type EN10MB (Ethernet), capture size 262144 bytes
35 packets captured
38 packets received by filter
0 packets dropped by kernel
ubuntu@develop:~$ ls /tmp
key  relayServers
ubuntu@develop:~$ cat /tmp/key
CLIENT_RANDOM b77461ec3a79fed38088f557acee97c2e0db9a4382d097af37906a33a3e5d65e a1f2ce1578b7303386a7942785a9bf0dfed15f523a86d4875ca71b86abaf76aa8dff0c24322c769e5fd65bca864d5cba

Résultat

Sur les recopies écrans ci-dessous, on peut voir :

Wireshark sans la clé. Pour lui, impossible de savoir ce qu'il y a dans les échanges chiffrés.



Cette fois, Wireshark possède la clé de chiffrement et peut donc déchiffrer les échanges.


That's all folks

  • 4 years 10 months before
  • |