Fin mars, Google a pris la décision unilatérale de retirer de son magasin de certificats l’autorité chinoise pour avoir frauduleusement émis des certificats au nom de google dans le cadre probable d’une proxyfication SSL.
Le problème reproché à MCS Holdings est que le certificat de sécurité a été généré au nom de Google sans aucune autorisation. Cela est contraire aux bonnes pratiques exigées dans les politiques de certification par le CA Browser Forum. Google aurait pu se limiter à révoquer le certificat intermédiaire (comme l’ont fait Microsoft et Mozilla). Mais finalement, Google a frappé plus fort : il rejette l’ensemble des certificats racines de CNNIC. Cette décision radicale et unilatérale a pour conséquence le rejet par cascade de tous les certificats qui ont été générés par l’entreprise.
L’impact devrait se faire sentir sur le web chinois d’autant plus que le navigateur chrome est particulièrement présent: notamment sur les smartphones.
Au delà de ce débat politico-philosophico-sécuritaire sur cette décision, une autre question se pose : Comment Google a-t-il été mis au courant de cette interception ?
L’utilisation du navigateur Google Chrome sur ces réseaux internes aurait-il permis l’exfiltration de données (une alerte de non conformité de la chaine des certificats utilisés) ? Google aurait-il mis un système en place dans son navigateurs pour s’assurer que ses services ne sont pas proxyfiés ?
La réponse est comme nous allons voir unanimement : Oui !
Tout d’abord, expliquons ici rapidement la notion de Public Key Pinning.
Le Public Key Pinning ou en bon Français l’épinglage de clé publique est un mécanisme de sécurité. Il consiste simplement à coder en dur dans un système la valeur que doit avoir telle clé publique. Dans le cas de Chrome, ce système est utilisé afin d’éviter tout usurpation par un tiers (légitime ou non) qui utiliserait un certificat différent (i.e non émis par l’IGC officielle) sur un service de Google et ce, même s’il est reconnu comme valide par le système sous-jacent. (Rappelons que par exemple sous Windows, Chrome utilise le magasin de certificat de l’OS, ce qui est bien différent de Firefox).
Donc Chrome embarque un système qui lui permet de détecter un homme du milieu. Mais cela ne nous dit pas comment la maison mère est informée d’une telle usurpation. Ainsi pour voir de quoi il retourne, nous prenons une grande respiration et nous plongeons dans le code source de Chromium (la version open source de Chrome) :
Une plongée à pic dans le code de Chrome nous conduit tout droit vers le fichier : chrome_fraudulent_certificate_reporter.cc, où nous trouvons des éléments très concrets sur la remontée d’information d’éventuels « certificats frauduleux ».
// TODO(palmer): Switch to HTTPS when the error handling delegate is more // sophisticated. Ultimately we plan to attempt the report on many transports. static const char kFraudulentCertificateUploadEndpoint[] = "http://clients3.google.com/log_cert_error";
Les commentaires dans le code sont également très clairs.
void ChromeFraudulentCertificateReporter::SendReport( const std::string& hostname, const net::SSLInfo& ssl_info) { // Do silent/automatic reporting ONLY for Google properties. For other // domains (when that is supported), Chrome will ask for user permission. if (!net::TransportSecurityState::IsGooglePinnedProperty(hostname)) return; certificate_reporter_->SendReport( CertificateErrorReporter::REPORT_TYPE_PINNING_VIOLATION, hostname, ssl_info); }
Du coup, ca devient assez explicite.
En pratique regardons ce que cela donne…
Dans un premier temps, installons un site web sur une machine de notre réseau, ainsi qu’un accès en SSL avec une chaine « maison » de certificats. Une fois le site en place, configurons une redirection DNS via /etc/hosts.
%> cat /etc/hosts 127.0.0.1 localhost 127.0.1.1 ubuntu 10.10.10.10 www.google.com # The following lines are desirable for IPv6 capable hosts ::1 ip6-localhost ip6-loopback fe00::0 ip6-localnet ff00::0 ip6-mcastprefix ff02::1 ip6-allnodes ff02::2 ip6-allrouters
Puis lançons Google Chrome.
Une fois que nous voulons accéder à la page https://www.google.com, Chrome nous affiche directement une erreur :
En parallèle, si nous observons le trafic avec Wireshark : il n’y a aucune communication vers clients3.google.com.
Si nous nous penchons davantage sur le sujet, nous tombons sur le blog Imperial Violet, qui explique que la mise en place de proxy SSL ne déclenchera pas d’alertes. Le Proxy SSL l’emporte sur l’épinglage de certificat. Il faut donc aller plus loin dans notre investigation.
Si l’on en croit le blog, pour passer outre le PKP, il faut simplement ne pas le faire avec des certificats émis par des ACs reconnues nativement par les navigateurs (comprendre validées par le CA Browser Forum) ou plus précisément ne pas utiliser de clé publique dont le hash est une collision avec l’une d’entre elles.
Ces hashs peuvent être obtenus de la sorte :
%> openssl x509 -inform DER -in GIAG2.crt -noout -pubkey | grep -v "\-\-\-\-" | base64 -d | sha1sum 43dad630ee53f8a980ca6efd85f46aa37990e0ea
Quatres options s’offrent à nous :
- Monter une AC et la faire valider par le CA Browser forum : Un peu long, un peu chère, un peu chiant pour juste faire un test qui nous fera radier du CA Browser forum
- Hacker un PSCE : bon, certains l’ont déjà fait… Mais c’est un peu compliqué et illégal.
- Modifier le code source de Chromium et le recompiler : 22Go de code source, ça fait beaucoup quand même…
- Et si on modifiait directement le code compilé de Chrome : ça c’est une méthode bien dans l’esprit des gros bourrins que nous sommes.
Nous allons donc tenter cette dernière. Commeçons par rechercher la chaine dans le binaire de Chrome sous linux :
%> xxd -p /opt/google/chrome/chrome | grep -c '43dad630ee53f8a980ca6efd' 1 %> bless /opt/google/chrome/chrome %> xxd -p /opt/google/chrome/chrome | grep -c '43dad630ee53f8a980ca6efd' 0 %> xxd -p /opt/google/chrome/chrome | grep -c '43dad6ddee53f8a980ca6efd' 1
Notez bien la différence sur la dernière ligne, nous avons modifié via l’éditeur hexadécimal bless le 4ème octet du hash de l’autorité de confiance de Google.
On relance Chrome qui redémarre sous aucun soucis (on notera ici que chrome n’est doc pas protégé en intégrité).
Tentons maintenant d’ouvrir Chrome et d’accéder au vrai http://www.google.com :
Chrome nous renvoie cette fois-ci une erreur différente mais qui semble plus convenir à ce que nous recherchons NET:ERR_SLL_PINNED_ KEY_NOT_IN_CERT _CHAIN.
De plus, nous observons une communication vers clients3.google.com : le site de délation pointé plus haut.
Voici en détails la communication HTTP entre le serveur et le client (en utilisant l’option « Follow TCP Steam »).
POST /log_cert_error HTTP/1.1 Host: clients3.google.com Connection: keep-alive Content-Length: 4684 Content-Type: x-application/chrome-fraudulent-cert-report User-Agent: Mozilla/5.0 (X11; Linux i686 (x86_64)) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2272.118 Safari/537.36 Accept-Encoding: gzip, deflate Accept-Language: en-US,en;q=0.8 .www.google.com..!-----BEGIN CERTIFICATE----- MIIEdjCCA16gAwIBAgIIavTkiJZO79swDQYJKoZIhvcNAQEFBQAwSTELMAkGA1UE BhMCVVMxEzARBgNVBAoTCkdvb2dsZSBJbmMxJTAjBgNVBAMTHEdvb2dsZSBJbnRl cm5ldCBBdXRob3JpdHkgRzIwHhcNMTUwNDIyMTM0NjQyWhcNMTUwNzIxMDAwMDAw WjBoMQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwN TW91bnRhaW4gVmlldzETMBEGA1UECgwKR29vZ2xlIEluYzEXMBUGA1UEAwwOd3d3 Lmdvb2dsZS5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDAB/W1 HE/ZA8NvB/oOrg6WN4zK6vdt9uqTpk3KAQzQ/e2qwy5WoewNmJgx9RPLIuHReTH+ Orh8vF1jk27rC89Pns9rRsE/FVjpaefNHi3trVFbDHcVdrU6JW0XjsG1+wbL8uSU vDMiey4ZNqUfopX3MJ6LHpFr5FgrRW1RsemTs/BfXjC4MoBdwnt6xoljh+KHzycy 8OgmCVXPONubyUKUeY/Ui9PaX0GWh5dE5uF72jG8NVPs67K7qpfmrdVSGHvRxH3P AwA90eKlakelqCSfcrZXD8q7EsMBQvRQbbFeuh/TexdiX+8hA1PXNMEURK5y2EBz 5jAbLuuLbQPN/DqZAgMBAAGjggFBMIIBPTAdBgNVHSUEFjAUBggrBgEFBQcDAQYI KwYBBQUHAwIwGQYDVR0RBBIwEIIOd3d3Lmdvb2dsZS5jb20waAYIKwYBBQUHAQEE XDBaMCsGCCsGAQUFBzAChh9odHRwOi8vcGtpLmdvb2dsZS5jb20vR0lBRzIuY3J0 MCsGCCsGAQUFBzABhh9odHRwOi8vY2xpZW50czEuZ29vZ2xlLmNvbS9vY3NwMB0G A1UdDgQWBBSgAQj1VB+R5iA9ZysggEXxg+oRFzAMBgNVHRMBAf8EAjAAMB8GA1Ud IwQYMBaAFErdBhYbvPZotXb1gba7Yhq6WoEvMBcGA1UdIAQQMA4wDAYKKwYBBAHW eQIFATAwBgNVHR8EKTAnMCWgI6Ahhh9odHRwOi8vcGtpLmdvb2dsZS5jb20vR0lB RzIuY3JsMA0GCSqGSIb3DQEBBQUAA4IBAQBlViFXxmddM6J6cVgUBHWw9nsSoJba SuwHE3Bfo9rrgzV80MazeDaM/tdVSCCA4jD/8jyJr3jl4wtazH9fkn7oBUxYEC22 Xy68txnKMu5LNze+eMbSs7MKSqcPd1ZdUm+3xcsnSc3bmvm/AuOd4WPueMhYdr4c qwUht+yFSBqEp856JjxsYDlX61h9i7Kq2EAPxA+9GvT2c5j9upUXmUYVnLrx4xjX LqbbahlsKd+fwvZZ7bFSuyFS8zs5HRfL1kuW1i79cHpuNqQmzC+DrFF2ouJi7uCR /A6Yfa2DF67oyHZKXuogVwko+cfVe91v+aAQVyltkzAcZy/xaypI8WFj -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- MIID8DCCAtigAwIBAgIDAjp2MA0GCSqGSIb3DQEBBQUAMEIxCzAJBgNVBAYTAlVT MRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMRswGQYDVQQDExJHZW9UcnVzdCBHbG9i YWwgQ0EwHhcNMTMwNDA1MTUxNTU1WhcNMTYxMjMxMjM1OTU5WjBJMQswCQYDVQQG EwJVUzETMBEGA1UEChMKR29vZ2xlIEluYzElMCMGA1UEAxMcR29vZ2xlIEludGVy bmV0IEF1dGhvcml0eSBHMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB AJwqBHdc2FCROgajguDYUEi8iT/xGXAaiEZ+4I/F8YnOIe5a/mENtzJEiaB0C1NP VaTOgmKV7utZX8bhBYASxF6UP7xbSDj0U/ck5vuR6RXEz/RTDfRK/J9U3n2+oGtv h8DQUB8oMANA2ghzUWx//zo8pzcGjr1LEQTrfSTe5vn8MXH7lNVg8y5Kr0LSy+rE ahqyzFPdFUuLH8gZYR/Nnag+YyuENWllhMgZxUYi+FOVvuOAShDGKuy6lyARxzmZ EASg8GF6lSWMTlJ14rbtCMoU/M4iarNOz0YDl5cDfsCx3nuvRTPPuj5xt970JSXC DTWJnZ37DhF5iR43xa+OcmkCAwEAAaOB5zCB5DAfBgNVHSMEGDAWgBTAephojYn7 qwVkDBF9qn1luMrMTjAdBgNVHQ4EFgQUSt0GFhu89mi1dvWBtrtiGrpagS8wEgYD VR0TAQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAQYwNQYDVR0fBC4wLDAqoCig JoYkaHR0cDovL2cuc3ltY2IuY29tL2NybHMvZ3RnbG9iYWwuY3JsMC4GCCsGAQUF BwEBBCIwIDAeBggrBgEFBQcwAYYSaHR0cDovL2cuc3ltY2QuY29tMBcGA1UdIAQQ MA4wDAYKKwYBBAHWeQIFATANBgkqhkiG9w0BAQUFAAOCAQEAJ4zP6cc7vsBv6JaE +5xcXZDkd9uLMmCbZdiFJrW6nx7eZE4fxsggWwmfq6ngCTRFomUlNz1/Wm8gzPn6 8R2PEAwCOsTJAXaWvpv5Fdg50cUDR3a4iowx1mDV5I/b+jzG1Zgo+ByPF5E0y8tS etH7OiDk4Yax2BgPvtaHZI3FCiVCUe+yOLjgHdDh/Ob0r0a678C/xbQF9ZR1DP6i vgK66oZb+TWzZvXFjYWhGiN3GhkXVBNgnwvhtJwoKvmuAjRtJZOcgqgXe/GFsNMP WOH7sf6coaPo/ck/9Ndx3L2MpBngISMjVROPpBYCCX65r+7bU2S9cS+5Oc4wt7S8 VOBHBw== -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- MIIDVDCCAjygAwIBAgIDAjRWMA0GCSqGSIb3DQEBBQUAMEIxCzAJBgNVBAYTAlVT MRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMRswGQYDVQQDExJHZW9UcnVzdCBHbG9i YWwgQ0EwHhcNMDIwNTIxMDQwMDAwWhcNMjIwNTIxMDQwMDAwWjBCMQswCQYDVQQG EwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEbMBkGA1UEAxMSR2VvVHJ1c3Qg R2xvYmFsIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2swYYzD9 9BcjGlZ+W988bDjkcbd4kdS8odhM+KhDtgPpTSEHCIjaWC9mOSm9BXiLnTjoBbdq fnGk5sRgprDvgOSJKA+eJdbtg/OtppHHmMlCGDUUna2YRpIuT8rxh0PBFpVXLVDv iS2Aelet8u5fa9IAjbkU+BQVNdnARqN7csiRv8lVK83Qlz6cJmTM386DGXHKTubU 1XupGc1V3sjs0l44U+VcT4wt/lAjNvxm5suOpDkZALeVAjmRCw7+OC7RHQWa9k0+ bw8HHa8sHo9gOeL6NlMTOdReJivbPagUvTLrGAMoUgRx5aszPeE4uwc2hGKceeoW MPRfwCvocWvk+QIDAQABo1MwUTAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBTA ephojYn7qwVkDBF9qn1luMrMTjAfBgNVHSMEGDAWgBTAephojYn7qwVkDBF9qn1l uMrMTjANBgkqhkiG9w0BAQUFAAOCAQEANeMpauUvXVSOKVCUn5kaFOSPeCpilKIn Z57QzxpeR+nBsqTP3UEaBU6bS+5Kb1VSsyShNwrrZHYqLizz/Tt1kL/6cdjHPTfS tQWVYrmm3ok9Nns4d0iXrKYgjy6myQzCsplFAMfOEVEiIuCl6rYVSAlk6l5PdPcF PseKUgzbFbS9bZvlxrFUaKnjaZC2mqUPuLk/IH2uSrW4nOQdtqvmlKXBx4Ot2/Un hw4EbNX/3aBd7YdStysVAq45pmp06drE57xNNB6pXE0zX5IJL4hmXXeXxx12E6nV 5fEWCRE11azbJHFwLJhWC9kXtNHjUStedejV0NxPNO3CBWaAocvmMw== -----END CERTIFICATE----- .........*..Rejecting public key chain for domain google.com. Validated chain: sha1/ jF2O7IYbEOOpLdqn7yuS0LThMH8=,sha256/4A1biTdE/o+f3r2TkTIWAVYq/SDuG+Y6bGpiVaMruPY=,sha1/ Q9rWMO5T+KmAym79hfRqo3mQ4Oo=,sha256/7HIpactkIAq2Y49orFOOQKurWxmmSFZhBCoQYcRhJ3Y=,sha1/ wHqYaI2J+6sFZAwRfap9ZbjKzE4=,sha256/h6801m+z8v3zbgkRHpq6L29Esgfzhj89C1SyUCOQmqU=, expected: sha1/vq7OyjSnqOco9nyMCDGdy77eijM=,sha1/Q9rW3e5T+KmAym79hfRqo3mQ4Oo= HTTP/1.1 200 OK Content-Type: text/html; charset=UTF-8 X-Content-Type-Options: nosniff Date: Fri, 08 May 2015 12:36:43 GMT Server: HTTP server (unknown) Cache-Control: private X-XSS-Protection: 1; mode=block X-Frame-Options: SAMEORIGIN Alternate-Protocol: 80:quic,p=1 Vary: Accept-Encoding Transfer-Encoding: chunked Accept-Ranges: none
Il ne s’agit ni plus ni moins que de la chaine complète des certificats utilisés pour authentifier le site. Donc nous venons de confirmer que Chrome dès lors qu’il détecte une usurpation de ces services via une AC légitime, transmet immédiatement toutes les informations à un serveur dédié chez Google.
Cependant si nous regardons le certificat présenté par le serveur au navigateur, celui-ci est parfaitement légitime :
Nous avons modifié en hexadécimal dans le code de Google-Chrome la valeur du hash de kSPKIHash_GoogleG2.
Et par défaut, nous avons ceci :
static const char kSPKIHash_GoogleG2[] = "\x43\xda\xd6\x30\xee\x53\xf8\xa9\x80\xca" "\x6e\xfd\x85\xf4\x6a\xa3\x79\x90\xe0\xea";
Cette valeur est utilisée plus loin :
static const char* const kGoogleAcceptableCerts[] = { kSPKIHash_GoogleBackup2048, kSPKIHash_GoogleG2, kSPKIHash_GeoTrustGlobal, NULL, };
Donc si vous modifiez la valeur du hash du SubjectPublicKeyInfo de l’AC de Google dans le binaire de Chrome, Lorsque le serveur présente sa chaine légitime de certificat, cette autorité n’est plus considérée comme de confiance en comparaison avec le hash que nous avons modifié.
Or cette AC est signée par GeoTrust Global CA qui est elle-même validé par le CA Browser Forum : Google interpréter cela comme une usurpation illégitime de ces services par un AC tiers non autorisée. Exactement ce qui s’est passé dans le cadre de l’affaire MCS Holdings.
Moralité si vous voulez mettre en place un proxy SSL sur les services de Google et rester discrets et sereins, pensez à faire un peu de filtrage.
A bon entendeur 🙂