1 Punkte von GN⁺ 2024-03-06 | 1 Kommentare | Auf WhatsApp teilen
  • Das Team von Texts.com wollte Meta Messenger für macOS analysieren, eine eigenständige Desktop-App, die der eigenen App ähnelt; Certificate Pinning verhinderte jedoch eine proxybasierte MITM-Analyse
  • Metas Certificate Pinning sorgt dafür, dass die App nur zugelassene Zertifikate vertraut, und blockiert damit das Abfangen und Entschlüsseln von Requests über eine vom Nutzer erstellte Zertifizierungsstelle
  • Dynamische Instrumentierung mit Frida verursachte bei Messenger Abstürze und war aufwendig zu verteilen, daher wählte das Team einen kleineren, reproduzierbaren Binary Patch
  • Die Analyse mit Hopper zeigte: Wenn man 4 Byte so ändert, dass IsUsingSandbox() true zurückgibt, lässt sich ein Codepfad nutzen, der bei Verwendung einer custom sandbox die SSL-Verifikation abschaltet
  • Nach dem Ersetzen des Original-Binaries durch die gepatchte ausführbare Datei und dem Umgang mit der Signierung konnten im Proxy-Tool Header, Response Body und Request-Informationen eingesehen werden

Warum Texts.com Messenger analysiert hat

  • Batuhan İçöz, der bei Texts.com für Projekte rund um Meta-Plattformen zuständig ist, hielt die Messenger-App für macOS für analysierenswert, weil sie als eigenständige Desktop-App dem eigenen Modell nahekommt
  • Das Abfangen von Netzwerk-Requests hat eine niedrige Einstiegshürde und eignet sich gut als erster Schritt, um das Verhalten einer App zu verstehen
  • Meta setzt in der App jedoch Certificate Pinning ein, um das Sicherheitsmodell zu stärken, und blockiert damit sogar MITM-Analysen, die Nutzer gegen sich selbst durchführen

Was Certificate Pinning verhindert

  • Um Requests mit einem Proxy-Client abzufangen, muss der Nutzer eine eigene Zertifizierungsstelle einrichten und ihr vertrauen
  • Über ein von dieser Zertifizierungsstelle ausgestelltes Zertifikat lassen sich Request-Informationen abfangen und entschlüsseln
  • Implementiert ein Dienst Certificate Pinning, akzeptiert die App nur Zertifikate, die von bestimmten Zertifizierungsstellen ausgestellt wurden
  • In diesem Fall ist das vom Nutzer erstellte Zertifikat ungültig, sodass sich Requests nicht abfangen lassen

Zustand vor dem Patch und Ziel

  • Ohne Abschalten des Certificate Pinning geben alle Requests „Internal Error“ zurück
  • In der Proxy-Software erscheint „SSL Handshake Failed“, und der Request-Lebenszyklus läuft nicht vollständig durch
  • In diesem Zustand ist es schwierig, den Inhalt der Requests abzuleiten
  • Ziel ist es, in Netzwerk-Debugging-Tools Requests, Responses und Header direkt lesen zu können

Fehlgeschlagene Umgehungen und die endgültige Wahl

  • Eine früher funktionierende Methode bestand darin, URL-Strings im Binary durch einen selbst gehosteten Endpoint zu ersetzen, der TLS nicht implementiert
    • Dieser Endpoint leitet Requests und Responses zwischen Client und Server weiter
    • Das eignet sich eher für kleine Apps als für große Apps wie Messenger
  • Auch dynamische Instrumentierungsbibliotheken wie Frida kamen infrage, waren bei Messenger aber wenig stabil
    • Beim Hooking kam es häufig zu Abstürzen
    • Wegen des Overheads war es schwierig, die problematischen Stellen zu finden
    • Die zum Ausführen nötige Umgebung und Tool-Konfiguration machen die Verteilung an Teammitglieder kompliziert
  • Auch ein über Jahre gepflegtes Frida-Skript wurde ausprobiert
    • Dieses Skript wurde für gängige Certificate-Pinning-Bibliotheken und Umgehungsmethoden genutzt und funktionierte bei den meisten Apps
    • Metas App-Familie gehörte nicht zu diesen „meisten“
  • Schließlich entschied sich das Team dafür, Certificate Pinning per Binary Patch vollständig abzuschalten, weil sich diese Lösung leicht an Teammitglieder weitergeben ließ

Mit Hopper gefundene Patch-Stelle

  • Nach dem Download von Messenger und dem Verschieben in den Programme-Ordner wurde das kompilierte ARM-Binary unter /Applications/Messenger.app/Content/MacOS/Messenger in Hopper importiert
  • Hopper kann kompilierte Binaries disassemblieren, dekompilieren, neu kompilieren, debuggen und visualisieren
  • Nach dem Laden des Binaries und der Referenzen wurde nach Begriffen wie certificate, ssl und pinning gesucht
  • Der String "SSL pinning verification failed for host:" wurde zum Ausgangspunkt der Analyse
  • Da ein kompiliertes Binary bei zu umfangreichen Änderungen abstürzen kann, wurde eine Strategie mit möglichst kleinen Änderungen gewählt
    • Ideal sind Patches mit begrenztem Wirkungsbereich, etwa das Umdrehen eines booleschen Werts, das Invertieren einer Bedingung oder das Ändern weniger Instruktionen

IsUsingSandbox() immer true zurückgeben lassen

  • Der Kontrollfluss wurde über einen Control-Flow-Graph visualisiert, und die verbundenen Referenzen wurden nach oben verfolgt
  • Dabei wurde der String "Using custom sandbox -> turn off SSL verification" gefunden
  • Die Referenz der Funktion, die dieses Flag bestimmt, wurde in der Datei gesucht und am Anfang der Prozedur identifiziert
  • In der Funktion IsUsingSandbox() wurde die Stelle verfolgt, an der der Rückgabewert zugewiesen wird
    • Das Register w0 wird aus w19 verschoben und anschließend zurückgegeben
    • w19 wird ursprünglich per load-byte-Instruktion zugewiesen
  • Wenn w19 nicht geladen, sondern immer auf true gesetzt wird, gibt IsUsingSandbox() true zurück
  • Dem zuvor gefundenen String zufolge wird bei Verwendung einer custom sandbox die SSL-Verifikation abgeschaltet; durch diese Änderung wird also Certificate Pinning deaktiviert
Original
ARM: ldrb w19, [sp, #0x40 + var_20]
HEX: F3 83 40 39

Rewritten
ARM: mov w19, #1
HEX: 33 00 80 52
  • Dieser Austausch wurde im hexadecimal mode durch direktes Ändern des Bytecodes der Anwendung vorgenommen

Ergebnis und erneute Signierung

  • Mit Hoppers Option „Produce New Executable“ wurde eine neue ausführbare Datei exportiert
  • Nachdem die Signatur der ausführbaren Datei entfernt war, wurde das ursprüngliche Messenger-Binary durch das neue Binary ersetzt
  • Nach dem erneuten Start von Messenger waren im Proxy-Tool Header, Response Body und weitere Request-Informationen sichtbar
  • Von insgesamt 97.477.728 Byte Binary-Größe reichte die Änderung von 4 Byte, um das Abfangen von Requests zu ermöglichen
  • Wer einen ähnlichen Ansatz unter iOS sehen möchte, kann Hassan Mostafas Artikel von 2020 zur Umgehung des Instagram Certificate Pinning lesen
    • Der Artikel beschreibt einen Fall, in dem auf einem jailbroken iPhone eine bedingte Sprunginstruktion umgedreht wurde, um Instagram Certificate Pinning zu deaktivieren
  • Das kompilierte Binary wurde an Batuhan weitergegeben
    • Batuhan beschaffte und installierte ein Signaturzertifikat und signierte anschließend die Anwendung
    • Danach konnte er das Binary auf seinem System verwenden, um seine eigenen Requests zu sehen
codesign --force --deep -s CERTNAME_OR_ID /Applications/Messenger.app

1 Kommentare

 
GN⁺ 2024-03-06
Hacker-News-Kommentare
  • Ich war auf einem ähnlichen Weg und habe in dem Moment aufgegeben, als ich dekompilieren/modifizieren/rekompilieren wollte.
    Das ist schon Hartnäckigkeit, und ich frage mich, wie viele Stunden tatsächlich hineingeflossen sind. Ich hatte mir eine Abbruchgrenze gesetzt und mich dann auch daran gehalten.

    • Dieser Beitrag war ursprünglich ein interner Texts.com-Post, und beim Aufbereiten zum Teilen habe ich den Teil weggelassen, dass ich vor ein paar Wochen exakt denselben Ansatz ausprobiert und wegen meines selbst gesetzten Zeitlimits aufgegeben hatte.
      Zuerst habe ich zwei Stunden lang verschiedene Befehle verändert und dann aufgegeben. Später sah ich einen älteren Beitrag des Reverse Engineers „Hassan Mostafa“ (cyclon3), der mit derselben Methode Erfolg hatte – nämlich einen Beitrag über den Einsatz von Hopper Disassembler bei Instagram auf iOS – und habe es an diesem Abend noch einmal versucht, aber ohne Erfolg. Ich habe auch dieselben Befehle gesucht und verändert.
      Dann beschloss ich, es gut sein zu lassen, und ein paar Wochen später probierte ich es aus einer Laune heraus mit leichtem Restfrust noch einmal; nachdem ich die Sandbox-Funktion gefunden hatte, war ich etwa 30 Minuten später fertig.
  • Mit eBPF scheint man Daten vor der TLS-Verschlüsselung lesen zu können: Debugging with eBPF Part 3: Tracing SSL/TLS connections https://blog.px.dev/ebpf-openssl-tracing/

    • Das ist eine praktische Methode, und andere Wege wie das Hooken von TLS-Sende-/Empfangsfunktionen mit Frida oder Ähnlichem sind fast sicher ebenfalls möglich. Der Vorteil eines Bypassings von Certificate Pinning ist jedoch, dass Forschende den Traffic mit bestehenden Tools wie Burp Suite oder mitmproxy leiten können.
      Wenn man den echten App-Traffic über einen Abfang-Proxy routet, spart das je nach Ziel erheblich Zeit. Wenn man zum Beispiel nach der Authentifizierung bzw. Session-Einrichtung automatisch einen Parameter einer Anfrage ändern will, ist es viel schneller, die App den gesamten Anfangsprozess selbst durchlaufen zu lassen und nur an einer Stelle im Proxy etwas zu ändern, statt einen neuen Client zu schreiben, der alles selbst ausführt, oder die Änderungslogik als eBPF-Filter zu implementieren.
    • Der Vollständigkeit halber: Bei Rust-Programmen, die die am weitesten verbreitete Rust-TLS-Bibliothek rustls statisch linken, funktioniert diese Methode nicht.
  • Sehr cleverer Ansatz. Allerdings hätte man Certificate Pinning wahrscheinlich auch im Sandbox-Modus erzwingen können.
    Das erinnert mich daran, wie ich im Studium versucht habe, Snapchat per Man-in-the-Middle anzugucken; dort gab es auch Certificate Pinning, und ich habe es am Ende nicht knacken können.

    • Ich habe dasselbe versucht und es geschafft, die App so zu patchen, dass ich Anfragen abfangen konnte, habe dann aber aufgegeben, als ich das Shared Object reverse engineeren wollte, das für die Request-Signatur zuständig war.
      Ich habe nicht einmal den Einstiegspunkt gefunden. Für eine relativ kleine Social-Media-App war die Sicherheit schon 2015 absurd stark.
    • Wenn Nutzer das Binärprogramm modifizieren können, ist es grundsätzlich schwierig, Certificate Pinning zu erzwingen.
      Selbst wenn im Sandbox-Modus Certificate Pinning verwendet worden wäre, hätte es wahrscheinlich einen anderen Weg gegeben, die Prüfung des angehefteten Zertifikats zu entfernen.
    • Stimmt. Man hätte es auch so implementieren können, dass innerhalb der Consumer-Funktion einfach nur die Ausgabe der Sandbox-Flag-Funktion auf true gesetzt wird; in diesem Fall hat die jetzige Methode aber auch gut funktioniert :)
    • Lustig, wie viele Leute offenbar genau daran bei mobilen Apps gescheitert sind.
      Zumindest früher.
  • Dieser Beitrag erinnert mich an die +Orc-Zeit. Viel Wissen, das damals üblich war – etwa wie man unerwünschte Branches findet und mit NOPs neutralisiert – scheint inzwischen verloren gegangen zu sein.
    Heute gibt es aber auch deutlich mehr Techniken zu lernen, also ist das nachvollziehbar.
    [1]: https://en.m.wikipedia.org/wiki/Old_Red_Cracker

    • Wenn +Orc oder Fravia (RIP) erwähnt werden, werde ich immer nostalgisch.
      Trotzdem gibt es meiner Meinung nach noch viele Leute, die NOP-Patches machen. Es ist nur komplexer geworden. Leute, die DRM knacken oder beliebige Mobile-Apps mit Hex-Editoren und Ähnlichem untersuchen, gibt es weiterhin.
      Moderne Programme sind komplexer und dadurch schwerer zugänglich geworden, aber gleichzeitig ist das nötige Wissen heute leichter verfügbar.
  • Wenn man Meta-App-Traffic abfangen will, muss man gar nicht unbedingt so weit gehen.
    https://www.facebook.com/whitehat/bugbounty-education/261571...

    • Das funktioniert nur auf Android. Uns ging es nicht darum, Android-Apps abzufangen.
  • Ich frage mich, ob Runtime-Binary-Checksums solche Modifikationen erschwert hätten.
    Ist das bei mobilen Apps nicht gängige Praxis? Bieten die iOS- oder Android-SDKs so etwas an? Es wirkt so, als müsste das an den offiziellen Release-Prozess angebunden und dann auf den jeweiligen nicht gejailbreakten Plattformen erzwungen werden.
    Ist vielleicht eine grundlegende Frage, aber die endgültige Lösung bestand ja darin, ein paar Bytes im Binärprogramm zu ändern, daher wirkte es grundsätzlich verhinderbar.

    • Es ist macOS (Desktop), nicht iOS (mobil).
    • Wenn man das Binärprogramm ohnehin modifiziert, muss man es neu signieren, was im Grunde denselben Effekt hat.
      Auf nicht gejailbreakten Plattformen geschieht das normalerweise mit einem Entwicklerzertifikat.
  • Metas Abwehr gegen Reverse Engineering wirkt, zumindest bei Messenger, ziemlich locker.
    Auch ohne auf fortgeschrittene Obfuskation zu gehen, hätte es einfach sein können, IsUsingSandbox() im Produktions-Build komplett zu entfernen.

    • Soweit ich aus meiner Zeit dort weiß, war Abwehr gegen Reverse Engineering nie ein Ziel.
      Certificate Pinning sollte es Angreifern schwerer machen, etwas zu manipulieren, nicht Nutzern.
    • Meta-Apps enthalten sogar in Produktions-Builds komplette Debug-Menüs. Der String, den der Autor gefunden hat, ist wahrscheinlich Teil eines solchen Menüs.
  • Als ich die App zum ersten Mal geknackt habe, ging ich natürlich davon aus, dass es scheitern würde, aber tatsächlich war es überraschend einfach, solche leicht zu modifizierenden JNE/JEZ-Stellen zu finden.
    Selbst wenn man die falsche auswählt, kann man einfach zur Originaldatei zurückkehren und eine andere Stelle ausprobieren.
    So etwas müsste sich mit AI doch leicht automatisieren lassen. Man müsste nur an mehreren Kandidatenstellen JEZ/JNZ umdrehen, die App starten und schauen, ob der nervige Screen erscheint.

    • Das ist nicht wirklich ein AI-Problem, sondern eher Fuzzing.
      Wenn die Fehlerbedingung sauber definiert ist, muss man letztlich nur die Kandidaten eingrenzen.
      Wenn AI Denuvo ignorierend im Nullschussverfahren knacken könnte, wäre das noch einmal etwas anderes.
    • Solche Tools gab es schon in den 90ern. Keine AI, einfach nur Brute Force.
  • Ich frage mich, warum die Anwendung eines so großen Unternehmens nicht vollständig obfuskiert ist und auch nicht genug Schutzmechanismen enthält, um die Ausführung modifizierter Binärdateien zu verhindern

    • Aus der Perspektive dessen, der diese Entscheidung ursprünglich bei der Facebook-App getroffen hat: Es lohnt sich nicht.
      Einzelpersonen, Gruppen oder Regierungen, die ausreichend kompetent oder stark genug motiviert sind, kommen am Ende ohnehin durch. So ist es nun einmal, wenn man Client-Binärdateien verteilt.
      Man kann enorm viel Zeit und Geld darauf verwenden, es zu verhindern. Früher wollte Pinterest einmal eine eigene Sprache und virtuelle Maschine ausliefern, und ich war dagegen. Oder man akzeptiert einfach, dass Client-Code im Grunde bereits kompromittiert ist, legt die Logik auf den Server und macht weiter.
      Certificate Pinning ist fast kostenlos und eher eine Maßnahme nach dem Motto „Einsteigen nur mit diesem Schlüssel oder besser“. Es ist nicht sicher, filtert aber allerlei nebenbei unternommene Versuche heraus
    • Obfuskation kostet, und Certificate Pinning dient eher dazu, Man-in-the-Middle-Angriffe zum Nachteil der Nutzer zu erschweren als Reverse Engineering zu verhindern.
      Natürlich hat es auch Einfluss auf Reverse Engineering, aber das ist eher ein Nebeneffekt.
      Letztlich läuft der Code auf dem Gerät des Nutzers, und der Nutzer kann beobachten, was der Code tut, daher ist Deobfuskation jederzeit möglich. Wenn eine Person es einmal entschlüsselt und das Ergebnis teilt, lässt es sich auch sehr leicht vervielfältigen. Das heißt nicht, dass Obfuskation nutzlos ist, aber es ist nichts, worauf man übermäßig viel Zeit verwenden sollte
    • Bei mobilen/Frontend-Apps bringt selbst das nichts.
      Wenn ein Angreifer physischen Zugriff auf das Gerät hat, gibt es ab diesem Zeitpunkt keine Möglichkeit mehr, ihn aufzuhalten. Man kann den Prozess nur lästiger machen und hoffen, dass er genervt aufgibt
    • Wahrscheinlich ist es eine Frage der Prioritäten und des Kosten-Nutzen-Verhältnisses.
      Obfuskation hätte das Ergebnis dieses Experiments kaum beeinflusst, allenfalls hätte sich der Ansatz dahin verschoben, etwas mehr dynamische Instrumentierung zu verwenden. Die wirksamste Obfuskation, die ich gesehen habe, war VM-Obfuskation, aber die Leistungseinbußen sind erheblich. Obfuskation erschwert auch normales Debugging.
      Der Schutz vor modifizierten Binärdateien findet auf Systemebene statt, kann aber auch auf Anwendungsebene implementiert werden und ist dort ebenfalls verbreitet. Allerdings kann auch diese Funktion umgangen werden, und nach Abschluss der Sicherheitsprüfungen kann man mit dynamischen Instrumentierungsbibliotheken wie Frida Änderungen vornehmen.
      Aus Sicht von Meta scheint es nicht die beste Verwendung der Ressourcen zu sein, mit Reverse Engineers ein Katz-und-Maus-Spiel zu spielen
    • Warum ist dann nicht jede Bank wie Fort Knox abgesichert?
  • Ich frage mich, welches Proxy-Tool im Artikel verwendet wurde. Wird der gesamte Anwendungsverkehr während der Laufzeit dorthin geleitet?
    Entschuldigung, falls das eine dumme Frage ist

    • Gute Frage. Im Artikel wurde Proxyman verwendet.
      Unter macOS lässt sich der gesamte Datenverkehr aller Anwendungen dorthin leiten, und wenn man auf dem Gerät ein selbstsigniertes Zertifikat installiert und sich mit dem Proxy verbindet, kann man auch iOS-Geräte darüber proxien