
Hallo, mein Name ist Andrey Batutin, ich bin Senior iOS Developer bei DataArt. In einem
früheren Artikel haben wir darüber gesprochen, wie Sie den Datenverkehr unserer mobilen Anwendung mithilfe eines HTTPS-Proxys abhören können. In diesem Abschnitt wird erläutert, wie Sie das SSL-Pinning umgehen können. Für alle Fälle empfehle ich, den ersten Artikel zu lesen, wenn Sie ihn noch nicht gelesen haben: Dies wird benötigt, um den folgenden Text zu verstehen.
Tatsächlich wird in der Praxis SSL-Pinning verwendet, sodass die beschriebene Methode zum Überprüfen und Ändern des Datenverkehrs einer mobilen Anwendung weder bösen Jungs noch einem neugierigen Koch zugänglich ist.
Was ist SSL-Pinning?
Im vorherigen Artikel haben wir das Charles Root-Zertifikat auf unserem Mobilgerät installiert, mit dem unser Charles Proxy Datenverkehr empfangen, entschlüsseln, anzeigen, zurückverschlüsseln und an Dropbox senden konnte.
Wenn ich als Entwickler einer mobilen Anwendung möchte, dass mein Datenverkehr nur von meinem Server und von niemand anderem überprüft wird, auch wenn dieser andere sein SSL-Zertifikat auf dem Gerät installiert hat, kann ich SSL-Pinning verwenden.
Das Wesentliche besteht darin, dass
der Client während des
SSL-Handshakes das vom Server empfangene Zertifikat überprüft.
In diesem Artikel wird die am einfachsten zu implementierende SSL-Pinning-Methode anhand einer zulässigen Liste von Zertifikaten beschrieben, die mit der Anwendung verbunden sind (Whitelisting).
Weitere Informationen zu SSL-Pinning-Typen finden Sie
hier .
SSL-Pinning-Implementierung bei FoodSniffer
Der vollständige Projektcode ist
hier . Zuerst müssen wir zwei Zertifikate im DER-Format für zwei Hosts erhalten:
Der zweite Server speichert JSON selbst mit einer Liste unserer Einkäufe.
Um die Zertifikate im richtigen Format zu erhalten, habe ich Mozila Firefox verwendet.
Öffnen Sie dropbox.com im Browser.
Klicken Sie auf das Schlosssymbol in der Adressleiste.


Klicken Sie auf Weitere Informationen und wählen Sie Sicherheit -> Zertifikat anzeigen.

Wählen Sie dann Details und suchen Sie das endgültige Zertifikat in der Zertifikatshierarchie.

Klicken Sie auf Exportieren und speichern Sie im DER-Format.

Wiederholen Sie den gleichen Vorgang für uc9b17f7c7fce374f5e5efd0a422.dl.dropboxusercontent.com.
HinweisDer Dropbox-Inhaltsserver (* .dl.dropboxusercontent.com) verwendet ein Platzhalterzertifikat. Das Zertifikat, das Sie für den Server uc9b17f7c7fce374f5e5efd0a422 extrahiert haben, ist daher für alle anderen Dropbox * .dl.dropboxusercontent.com-Server geeignet.
Als Ergebnis habe ich zwei Dateien mit Zertifikaten erhalten:
dropboxcom.crt ,
dldropboxusercontentcom.crt ,
was ich dem FoodSniffer iOS App Projekt hinzugefügt habe.

Dann habe ich die Erweiterung für die FoodListAPIConsumer-Klasse hinzugefügt, in der ich das vom Server empfangene Zertifikat überprüfe. Dazu suche ich in der Liste der zulässigen Zertifikate danach und verarbeite den Authentication Challenge-Delegaten des NSURLSessionDelegate-Protokolls:
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 } }
Im
Zertifikat- Array habe ich Datendarstellungen meiner zulässigen Zertifikate.
Wenn Charles Proxy ausgeführt wird, wird die Verbindung der Anwendung getrennt, da das Charles-Zertifikat nicht in der Liste der zulässigen Zertifikate enthalten ist. Dem Benutzer wird der folgende Fehler angezeigt:

Hacker besiegt!
Aber jetzt gibt es ein kleines Problem: Wie kann ich als Entwickler den HTTPS-Verkehr meiner eigenen Anwendung überwachen?
Frida
Eine Möglichkeit besteht darin, das SSL-Pinning mit dem dynamischen Code-Injection-Framework des
Frida- Frameworks zu deaktivieren.
Die Idee ist, dass die
validateTrustCertificateList- Methode während der Anwendungsentwicklung immer true zurückgibt.
Dies kann natürlich ohne dynamische Code-Injection erreicht werden, z. B. unter Verwendung der Bedingung
#if targetEnvironment (Simulator) , um das SSL-Pinning auf dem Simulator zu deaktivieren, ist jedoch zu einfach.
Mit Frida können wir ein JavaScript-Skript schreiben (clever, oder?), In dem wir die Implementierung von validateTrustCertificateList durch ein Skript ersetzen, das immer true zurückgibt.
Dieses Skript wird in der Ausführungsphase in die Anwendung eingefügt.
Wie Frida unter iOS funktioniert, können Sie
hier lesen.
sudo pip installiere frida-toolsFrida-Schrift
Das direkte Skript zum Ersetzen der Funktion validateTrustCertificateList sieht folgendermaßen aus:
// 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();
- Wir finden den Zeiger auf validateTrustCertificateList in der Anwendungsbinärdatei mit dem vollständigen Namen der Funktion.
- Wir verpacken den Zeiger in einen NativeFunction-Wrapper, der den Typ des Parameters und den Ausgabewert der Funktion angibt.
- Ersetzen Sie die Implementierung der Funktion validateTrustCertificateList durch eine, die immer 1 zurückgibt (d. H. True).
Das gesamte Skript befindet sich in
{source_root} /fridascrpts/killCertPinnig.js .
Eines der Probleme ist, wie der vollständige Name der Funktion
_T016FoodSnifferFrida0A15ListAPIConsumerC024validateTrustCertificateD0SbSo03SecG0CF erhalten wurde
Dafür habe ich die folgende Technik verwendet.
- Erstellt ein zusätzliches Ziel FoodSnifferFrida in der Anwendung.
- Ich habe die FridaGadget.dylib- Bibliothek damit verbunden, die ich hier aufgenommen habe. Das Verfahren zum Verbinden der Bibliothek wird hier ausführlicher beschrieben.
- Startete die FoodSniffer-Anwendung auf dem Simulator.
- Mit diesem Befehl wurde der vollständig qualifizierte Funktionsname validateTrustCertificateList ermittelt :
frida-trace -R -f re.frida.Gadget -i "* validateTrust *" - Habe es in der Form:

Und dann in
killCertPinnig.js verwendet .
Warum so ein „seltsamer“ Name am Ende zu Ende ging und was all diese T016 und 0A15 bedeuten, kann man
hier sehen.
SSL Killing Pinning
Starten Sie jetzt endlich FoodSniffer mit deaktiviertem SSL Pinnig!
Starten Sie Charles Proxy.
Führen Sie das Ziel FoodSnifferFrida im Xcode-Projekt im Simulator aus. Wir sollten nur einen weißen Bildschirm sehen. Die Anwendung wartet darauf, dass Frida eine Verbindung herstellt.

Führen Sie Frida aus, um das Skript
killCertPinnig.js auszuführen:
frida -R -f re.frida.Gadget -l ./fridascrpts/killCertPinnig.jsWarten wir auf die Verbindung zur iOS-Anwendung:

Setzen Sie die Anwendung mit dem Befehl% resume fort:

Jetzt sollten wir die Liste der Lebensmittel in der Anwendung sehen:

Und JSON in Charles Proxy:

Gewinn!
Fazit
Frida ist wie Wireshark für Binärdateien. Es funktioniert auf iOS-, Android-, Linux- und Windows-Plattformen. Mit diesem Framework können Sie Aufrufe von Methoden und Funktionen verfolgen - sowohl vom System als auch vom Benutzer. Ersetzen Sie auch die Werte von Parametern, Rückgabewerten und die Implementierung von Funktionen.
Das Umgehen von SSL-Pinning in einer Entwicklungsumgebung mit Frida scheint etwas
übertrieben . Es zieht mich an, weil ich für das Debuggen und die Anwendungsentwicklung keine spezifische Logik in der Anwendung haben muss. Diese Logik überfrachtet den Code und kann bei falscher Implementierung in die Release-Version der Assembly gelangen (Makros, hallo an Sie!).
Darüber hinaus ist Frida für Android anwendbar. Dies gibt mir die Möglichkeit, meinem gesamten Team das Leben zu erleichtern und einen reibungslosen Entwicklungsprozess für die gesamte Produktlinie sicherzustellen.
Frida positioniert sich als Black-Box-Tool zur Prozesscode-Injektion. Damit ist es möglich, ohne den direkten Code der iOS-Anwendung zu ändern, die Protokollierung von Methodenaufrufen zur Laufzeit hinzuzufügen, was beim Debuggen komplexer und seltener Fehler unverzichtbar sein kann.