
Bonjour, je m'appelle Andrey Batutin, je suis développeur iOS senior chez DataArt. Dans un
article précédent, nous avons expliqué comment vous pouvez détecter le trafic de notre application mobile à l'aide d'un proxy HTTPS. Dans ce document, nous verrons comment contourner l'épinglage SSL. Au cas où, je vous recommande de lire le premier article si vous ne l'avez pas encore lu: cela sera nécessaire pour comprendre le texte ci-dessous.
En fait, dans la pratique, l'épinglage SSL est utilisé pour que la méthode décrite pour inspecter et modifier le trafic d'une application mobile ne soit pas accessible aux méchants ou aux chefs curieux.
Qu'est-ce que l'épinglage SSL
Dans l'article précédent, nous avons installé le certificat racine Charles sur notre appareil mobile, ce qui a permis à notre proxy Charles de recevoir, de décrypter, de nous montrer le trafic, de le crypter et de l'envoyer à Dropbox.
Si, en tant que développeur d'une application mobile, je souhaite que mon trafic soit inspecté uniquement par mon serveur et personne d'autre, même si celui-ci a installé son certificat SSL sur l'appareil, je peux utiliser l'épinglage SSL.
Son essence se résume au fait que lors de la prise de
contact SSL, le client vérifie le certificat reçu du serveur.
Cet article décrit la méthode d'épinglage SSL la plus simple à implémenter à l'aide d'une liste autorisée de certificats câblés dans l'application (liste blanche).
Pour en savoir plus sur les types d'épinglage SSL,
cliquez ici .
Implémentation de l'épinglage SSL chez FoodSniffer
Le code de projet complet est
ici . Tout d'abord, nous devons obtenir deux certificats au format DER pour deux hôtes:
Le deuxième serveur stocke JSON lui-même avec une liste de nos achats.
Pour obtenir les certificats au bon format, j'ai utilisé Mozila Firefox.
Ouvrez dropbox.com dans le navigateur.
Cliquez sur le symbole du cadenas dans la barre d'adresse.


Cliquez sur Plus d'informations, sélectionnez Sécurité -> Afficher le certificat.

Sélectionnez ensuite Détails et recherchez le certificat final dans la hiérarchie des certificats.

Cliquez sur Exporter et enregistrez au format DER.

Répétez la même procédure pour uc9b17f7c7fce374f5e5efd0a422.dl.dropboxusercontent.com.
RemarqueLe serveur de contenu Dropbox (* .dl.dropboxusercontent.com) utilise un certificat générique. Ainsi, le certificat que vous avez extrait pour le serveur uc9b17f7c7fce374f5e5efd0a422 conviendra à tout autre serveur Dropbox * .dl.dropboxusercontent.com.
En conséquence, j'ai obtenu deux fichiers avec des certificats:
dropboxcom.crt ,
dldropboxusercontentcom.crt ,
que j'ai ajouté au projet d'application iOS FoodSniffer.

J'ai ensuite ajouté l'extension pour la classe FoodListAPIConsumer, dans laquelle je vérifie le certificat reçu du serveur. Pour ce faire, je le cherche dans la liste des certificats autorisés, en traitant le délégué Authentication Challenge du protocole NSURLSessionDelegate:
extension FoodListAPIConsumer { func urlSession(_ session: URLSession, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) { guard let trust = challenge.protectionSpace.serverTrust else { completionHandler(.cancelAuthenticationChallenge, nil) return } let credential = URLCredential(trust: trust) if (validateTrustCertificateList(trust)) { completionHandler(.useCredential, credential) } else { completionHandler(.cancelAuthenticationChallenge, nil) } } func validateTrustCertificateList(_ trust:SecTrust) -> Bool{ for index in 0..<SecTrustGetCertificateCount(trust) { if let certificate = SecTrustGetCertificateAtIndex(trust, index){ let serverCertificateData = SecCertificateCopyData(certificate) as Data if ( certificates.contains(serverCertificateData) ){ return true } } } return false } }
Dans le tableau des
certificats , j'ai des représentations de données de mes certificats autorisés.
Désormais, lorsque Charles Proxy est en cours d'exécution, l'application se déconnecte car le certificat Charles n'est pas inclus dans la liste des permis. L'utilisateur verra l'erreur suivante:

Les pirates ont vaincu!
Mais maintenant, il y a un petit problème - comment puis-je, le développeur, surveiller le trafic HTTPS de ma propre application?
Frida
Une option consiste à désactiver l'épinglage SSL avec le cadre d'injection de code dynamique du cadre
Frida .
L'idée est que la méthode
validateTrustCertificateList renvoie toujours true pendant le développement de l'application.
Bien sûr, cela peut être réalisé sans injection de code dynamique, par exemple, en utilisant la condition
#if targetEnvironment (simulator) pour désactiver l'épinglage SSL sur le simulateur, mais c'est trop simple.
En utilisant Frida, nous pouvons écrire un script JavaScript (intelligemment, non?) Dans lequel nous remplaçons l'implémentation de validateTrustCertificateList par une qui renvoie toujours true.
Et ce script sera injecté dans l'application au stade de l'exécution.
Comment Frida fonctionne sur iOS, vous pouvez lire
ici .
Installation de Frida (prise d'ici ).
sudo pip installe frida-toolsÉcriture de Frida
Le script direct pour remplacer la fonction validateTrustCertificateList ressemble à ceci:
// Are we debugging it? DEBUG = true; function main() { // 1 var ValidateTrustCertificateList_prt = Module.findExportByName(null, "_T016FoodSnifferFrida0A15ListAPIConsumerC024validateTrustCertificateD0SbSo03SecG0CF"); if (ValidateTrustCertificateList_prt == null) { console.log("[!] FoodSniffer!validateTrustCertificateList(...) not found!"); return; } // 2 var ValidateTrustCertificateList = new NativeFunction(ValidateTrustCertificateList_prt, "int", ["pointer"]); // 3 Interceptor.replace(ValidateTrustCertificateList_prt, new NativeCallback(function(trust) { if (DEBUG) console.log("[*] ValidateTrustCertificateList(...) hit!"); return 1; }, "int", ["pointer"])); console.log("[*] ValidateTrustCertificateList(...) hooked. SSL pinnig is disabled."); } // Run the script main();
- Nous trouvons le pointeur pour validateTrustCertificateList dans le binaire d'application par le nom complet de la fonction.
- Nous enveloppons le pointeur dans un wrapper NativeFunction, indiquant le type du paramètre et la valeur de sortie de la fonction.
- Remplacez l'implémentation de la fonction validateTrustCertificateList par une qui renvoie toujours 1 (c'est-à-dire true).
Le script entier se trouve dans
{source_root} /fridascrpts/killCertPinnig.js .
L'un des problèmes est de savoir comment le nom complet de la fonction
_T016FoodSnifferFrida0A15ListAPIConsumerC024validateTrustCertificateD0SbSo03SecG0CF a été
obtenu.Pour cela, j'ai utilisé la technique suivante.
- Création d'une cible FoodSnifferFrida supplémentaire dans l'application.
- J'y ai connecté la bibliothèque FridaGadget.dylib , que j'ai prise ici. La procédure de connexion à la bibliothèque est décrite plus en détail ici.
- Lancement de l'application FoodSniffer sur le simulateur.
- Utilisez cette commande pour trouver le nom de fonction complet validateTrustCertificateList :
frida-trace -R -f re.frida.Gadget -i "* validateTrust *" - Je l'ai sous la forme:

Et puis utilisé dans
killCertPinnig.js .
La raison pour laquelle un nom aussi «étrange» a finalement fini et ce que signifient tous ces T016 et 0A15 peut être vue
ici .
SSL Killing Pinning
Maintenant, lancez enfin FoodSniffer avec SSL Pinnig désactivé!
Lancez Charles Proxy.
Exécutez le FoodSnifferFrida cible dans le projet Xcode du simulateur. Nous devrions voir juste un écran blanc. L'application attend que Frida s'y connecte.

Exécutez Frida pour exécuter le script
killCertPinnig.js :
frida -R -f re.frida.Gadget -l ./fridascrpts/killCertPinnig.jsAttendons la connexion à l'application iOS:

Continuez l'application à l'aide de la commande% resume:

Maintenant, nous devrions voir la liste des aliments dans l'application:

Et JSON dans Charles Proxy:

Profit!
Conclusion
Frida est comme Wireshark pour les binaires. Il fonctionne sur les plates-formes iOS, Android, Linux et Windows. Ce framework vous permet de suivre les appels aux méthodes et fonctions - système et utilisateur. Et aussi remplacer les valeurs des paramètres, les valeurs de retour et la mise en œuvre des fonctions.
Contourner SSL L'épinglage dans un environnement de développement utilisant Frida peut sembler un peu
exagéré . Cela m'attire parce que je n'ai pas besoin d'avoir une logique spécifique dans l'application pour le débogage et le développement d'applications. Cette logique encombre le code et, si elle n'est pas implémentée correctement, peut s'infiltrer dans la version finale de l'assembly (macros, bonjour à vous!).
De plus, Frida est applicable pour Android. Ce qui me donne l'opportunité de faciliter la vie de toute mon équipe et d'assurer un processus fluide de développement de toute la gamme de produits.
Frida se positionne comme un outil d'injection de code de processus de boîte noire. Avec lui, il est possible, sans changer le code direct de l'application iOS, d'ajouter des appels de méthode de journalisation à l'exécution, ce qui peut être indispensable lors du débogage de bogues complexes et rares.