Eine saudi-arabische Regierungs-App enthielt den privaten Schlüssel einer Bank. Um den Fehler zu melden, musste man in Saudi-Arabien ansässig sein.

@iam_zachi
ENGLISCH08. Sept. 2026
235K
1.5K
95
28
1.1K

TL;DR

Ein Sicherheitsforscher entdeckte, dass die offizielle saudi-arabische Nusuk-App einen privaten RSA-Schlüssel sowie OAuth-Anmeldedaten der Saudi National Bank preisgab, die lediglich durch ein einstelliges Passwort geschützt waren. Aufgrund von geoblockierten Schwachstellen-Portalen war ein viraler Tweet notwendig, um den Fehler zu melden.

Eine 2.835 Byte große Datei in einer staatlichen App mit über 10 Millionen Installationen enthielt ein aktives Client-Zertifikat der Saudi National Bank. Es brauchte einen viralen Tweet, um jemanden dazu zu bringen, sich das anzusehen.

Zusammenfassung

Die offizielle Nusuk-App (com.moh.nusukapp, Ministerium für Hadsch und Umra, über 10 Millionen Installationen, mit dem Google Play-Siegel "Staatliche App") enthielt eine PKCS#12-Datei mit einem privaten RSA-Schlüssel und einem Client-Zertifikat, ausgestellt von der Saudi National Bank. Das Passwort für diese Datei war nur wenige Zeilen entfernt im Code der App selbst hartcodiert. Es bestand aus einem einzigen Zeichen: 2.

Daneben standen im Klartext die OAuth2-Client-ID und das Client-Secret für die Banking-as-a-Service-API der Bank, die die Berechtigungen identity accounts cards verification kyc cardpay transfers anforderte.

Jeder, der die App aus dem Google Play Store heruntergeladen hatte, hatte Zugriff auf all diese Daten.

Ich versuchte, dies zu melden. Mir wurde mitgeteilt, dass das Schwachstellenportal nur für Nutzer innerhalb Saudi-Arabiens zugänglich sei. Also twitterte ich darüber. Der Tweet erzielte 1,5 Millionen Aufrufe, und plötzlich wollte dasselbe Portal Details wissen. Einen Tag später waren die Anmeldedaten aus der App entfernt.

https://x.com/iam_zachi/status/2094445016194207745

Dieser Bericht behandelt nur den Banken-Fund. Alles unten Aufgeführte wurde in der aktuellen App behoben, und der Anbieter gibt an, dass die Anmeldedaten rotiert wurden.

Was Nusuk ist

Nusuk ist die offizielle Plattform der saudischen Regierung für Hadsch und Umra. Sie verwaltet Pilgererlaubnisse, E-Visa, Buchungen und die Nusuk-Karte. Sie wird vom Ministerium für Hadsch und Umra betrieben, ist auf Google Play als verifizierte staatliche App gekennzeichnet und hat über zehn Millionen Installationen. Sie enthält außerdem eine Wallet-Funktion, Nusuk Wallet, die zusammen mit der Saudi National Bank entwickelt und von SAMA, der saudischen Zentralbank, genehmigt wurde. Um diese Wallet geht es in diesem Artikel.

Der Fund

Ich lud das APK-Set direkt aus dem Google Play Store herunter (Version 17.4.9, versionCode 131215) und entpackte es. Nichts Außergewöhnliches: Standard Kotlin/Compose, kein Packer, keine nennenswerte Verschleierung.

In den Ressourcen der App, unter res/raw/nusuk.pfx, befand sich ein 2.835 Byte großer PKCS#12-Container. Eine .pfx-Datei ist das Standard-Bundle-Format für ein Zertifikat plus seinen privaten Schlüssel, verschlüsselt mit einem Passwort.

Das Passwort stand im Code der App, nur wenige Zeilen von der Stelle entfernt, an der die Datei geladen wird:

text
1const-string v3, "2"

Ein einzelnes Zeichen, als Literal im dekompilierten Bytecode. Ein openssl-Aufruf später:

text
1RSA private key, 2048 bit, 2 prime factors
2Subject: C=SA, ST=Jeddah, L=Jeddah, O=Nusuk.sa, CN=eshbeata@staq.io
3Issuer: C=SA, ST=Riyadh, L=Riyadh, O=The Saudi National Bank, OU=Finto,
4 CN=Application Issuer
5Serial: 0x24 (36)
6Valid: 2026-04-27 -> 2027-04-27
7Extended Key Usage (critical): TLS Web Client Authentication

Ein aktives Client-Zertifikat, ausgestellt von einer Bank, gültig für ein weiteres Jahr, gedacht zur Authentifizierung eines Clients gegenüber einem Server über TLS.

Es war nicht allein. Derselbe Codepfad ist die Wallet-Komponente, verpackt als com.walletstaq und eingebunden in das Trustless SDK von Staq Technologies, dem Unternehmen, das die Finto BaaS-Plattform der SNB betreibt. Drei weitere Werte standen dort im Klartext:

mit dem angeforderten OAuth-Bereich:

text
1identity accounts cards verification kyc cardpay transfers

Beide Anmeldedaten wurden außerdem zu einem einzigen String verkettet und an einen Debug-Logger übergeben, zusammen mit der Basis-URL.

(Ich veröffentliche weder das Schlüsselmaterial noch die vollständigen Geheimniswerte. Was zählt, ist die Form des Problems.)

Warum das schlimm ist, in einfachen Worten

Stellen Sie sich die API der Bank als eine Tür mit zwei Schlössern vor.

Das erste Schloss ist gegenseitiges TLS (mTLS). Normalerweise weist ein Server Ihnen gegenüber seine Identität mit einem Zertifikat nach. Bei mTLS müssen Sie auch dem Server gegenüber Ihre Identität mit einem Zertifikat nachweisen. Das ist die .pfx-Datei: das Zertifikat und der private Schlüssel, der beweist, dass Sie es besitzen. Es soll das Ding sein, das nur das legitime Client-System hat.

Das zweite Schloss ist das OAuth-Client-Secret, das Passwort, das die Anwendung verwendet, um bei der Bank-API einen Zugriffstoken anzufordern.

Beide Schlösser wurden in einer kostenlosen App im Google Play Store ausgeliefert, und der Schlüssel zur Kiste, die das erste enthielt, war die Ziffer 2.

Ich bestätigte, dass der Zielhost tatsächlich mTLS erzwingt: Der TLS-Handshake mit api.baas.alahli.com fordert Client-Zertifikate an (er sendet Acceptable client certificate CA names), und das Server-Zertifikat lautet CN=*.baas.alahli.com, O=The Saudi National Bank. Es handelte sich also nicht um ein dekoratives Zertifikat für eine Testumgebung. Es war die Berechtigung für die Haustür einer Produktions-Banking-API, mit Berechtigungen, die Identität, Konten, Karten, KYC, Kartenzahlungen und Überweisungen abdecken.

Unabhängig vom Leck gibt es ein darunterliegendes Designproblem. Ein Client-Zertifikat, das identisch an zehn Millionen Geräte ausgeliefert wird, kann eine Installation nicht von einer anderen unterscheiden. Jede Kopie präsentiert dieselbe Berechtigung, sodass das Zertifikat der Bank sagt, welche App aufruft, aber nichts darüber, wer aufruft. Anmeldedaten für eine solche API gehören hinter Ihren eigenen Backend-Server: Die App spricht mit Ihrem Server, Ihr Server spricht mit der Bank.

Was ich nicht getan habe

Ich führte genau eine Prüfung gegen den Token-Endpunkt (POST /api/tppa/token) durch, um zu sehen, ob die Anmeldedaten aktiv waren. Er gab HTTP 403 von nginx zurück. Das tat auch eine Anfrage ohne Zertifikat, und das tat auch die bloße Root-URL. Das ist eine netzwerkseitige Sperre vor der API, fast sicher geografisch, und sie sagt nichts darüber aus, ob die Anmeldedaten funktionieren.

Von außerhalb Saudi-Arabiens konnte ich nicht feststellen, ob diese Anmeldedaten aktiv waren. Ich hörte dort auf. Alles Weitere wäre ein Versuch gewesen, die Zugangskontrolle einer Bank zu umgehen, und der Fund hängt nicht davon ab: Ein privater Schlüssel und ein Banking-OAuth-Secret mit Überweisungsberechtigung in einem öffentlich herunterladbaren Artefakt sind der Fund, unabhängig davon, ob ich persönlich den Endpunkt erreichen kann.

Der Versuch, es zu melden

Dies ist der Teil, der den Tweet viral gehen ließ, und er ist die interessantere Hälfte.

Ich suchte nach einem Weg, dies verantwortungsvoll zu melden. Was existiert:

Kanal

Ergebnis

security.txt auf nusuk.sa, haj.gov.sa, hajj.nusuk.sa

Existiert nicht

Saudi CERT (cert.gov.sa) Schwachstellenmelde-Seite

Leitet zu NCA weiter; eigene Melde-Seite abgeschaltet

NCA-Schwachstellenformular (haseen.gov.sa)

Von Deutschland aus nicht erreichbar: Timeout, geografisch gesperrt

bugbounty.sa

Geschlossenes Programm, HTTP 403 von außen

HackerOne / Bugcrowd

Kein Programm für Nusuk, das Ministerium oder Elm

App-Store-Eintragskontakte

Support-Adressen, kein Sicherheitsmandat

Nichts davon hinterließ mir einen Kanal mit einem Sicherheitsmandat, den ich tatsächlich erreichen konnte. Ich mailte trotzdem. Die Antwort vom Haseen-Support:

"Der Zugang zum Haseen-Portal ist auf Benutzer innerhalb des Königreichs Saudi-Arabien beschränkt. Für weitere Anfragen können Sie uns über den 'Wir kümmern uns'-Dienst auf dem offiziellen Haseen-Portal kontaktieren."

Das Haseen-Portal, das ich nicht erreichen kann. Ich schrieb zurück und erklärte, dass ich kein Saudi bin, dass es sich um ein privates Bankzertifikat handelt, das in einer staatlichen App offengelegt wurde, und dass ich es nur übergeben wollte. Die Antwort war erneut, dass das Formular nur für Bürger des KSA funktioniert.

Also reichte ich einen Bericht bei CERT/CC über deren VINCE-Plattform als koordinierende Vermittlungsstelle ein (VRF#26-08-DXMKL), beschränkt auf diesen einen Fund. Das ist der Weg, den man geht, wenn die betroffene Partei keinen erreichbaren eigenen Kanal hat.

Und dann twitterte ich darüber, hauptsächlich aus Frustration.

Der Tweet erzielte 1,5 Millionen Aufrufe. Innerhalb weniger Stunden mailte mir der Haseen-Support unaufgefordert im selben Thread, der mir zweimal gesagt hatte, das Portal sei nicht für mich:

"Laut unserem zuständigen Team teilen Sie uns bitte weitere Details zu der Sicherheitslücke mit."

Ich sandte die vollständigen Details. Mir ist es lieber, das Problem wird behoben, als dass ich mit dem Prozess recht habe.

Die Behebung

Das nächste App-Update erschien sowohl für Android als auch für iOS. Ich lud den neuen Android-Build (17.5.0, versionCode 156635) direkt aus dem Play Store herunter und verglich ihn mit dem, was ich analysiert hatte. Drei Prüfungen:

  1. Kein Zertifikatscontainer im neuen APK-Set: keine Datei mit der Endung .pfx, .p12, .pkcs12, .jks, .bks, .pem oder .key. Ich hashte auch das alte nusuk.pfx und verglich es Byte für Byte mit jeder gleich großen Datei im neuen Build, falls es einfach umbenannt worden war. Keine Übereinstimmung.
  2. Keine der bekannten Anmeldedaten. Ich suchte die exakten alten Werte für Basis-URL, Bereich, Client-ID und Client-Secret in allen DEX-Dateien, nativen Bibliotheken, Assets, XML, JSON und Rohressourcen. Null Treffer für alle vier.
  3. Kein Codepfad. Version 17.4.9 enthielt 4.571 Dateien unter den Paketen com.walletstaq und com.trustless; 17.5.0 enthält null. Die Marker baas.alahli, tppa/token, nusuk.pfx, der Zertifikatsbetreff und der Ausstellername ergeben null Treffer im dekodierten Build.

Die gesamte Wallet- und BaaS-Integration wurde entfernt. Die Umbenennungsprüfung in Schritt 1 schließt aus, dass die Werte einfach an eine andere Stelle im Paket verschoben wurden.

Der Teil, den niemand von außen überprüfen kann

Das Entfernen eines Geheimnisses aus einer App macht das Geheimnis nicht ungültig. Alte APK-Kopien bleiben für immer verfügbar, und das Zertifikat war bis April 2027 gültig. Die offene Frage ist also, ob es widerrufen wurde und ob das OAuth-Secret rotiert wurde.

Ich suchte nach einem Weg, dies unabhängig zu überprüfen. Es gibt keinen, und der Grund dafür ist selbst ein Fund.

Das Zertifikat trägt keine crlDistributionPoints-Erweiterung, daher wird überhaupt keine Sperrliste referenziert. Sein einziger Sperr-Endpunkt ist:

text
1OCSP - URI: http://finto-ocsp-responder.prod.svc.cluster.local:8080/api/v1/ocsp

.cluster.local ist das interne DNS-Suffix eines Kubernetes-Clusters. Es ist per Definition im öffentlichen Internet nicht routbar und löst von außerhalb dieses Clusters über einfaches HTTP auf Port 8080 mit NXDOMAIN auf.

Der Sperrstatus dieses Zertifikats kann also nicht von außerhalb der Bank-Infrastruktur überprüft werden, da es hier draußen nichts gibt, was man abfragen könnte. Für jeden vertrauenden Teilnehmer außerhalb dieses einen Clusters sind Zertifikate dieses Ausstellers praktisch nicht widerrufbar, was allein schon einen Absatz in der Architekturüberprüfung von jemandem verdient.

Dasselbe Feld veröffentlichte auch den Clusternamen, den Namespace, den Dienstnamen und den Port der Produktions-PKI der BaaS-Plattform einer Bank in einer App, die an zehn Millionen Menschen verteilt wurde.

Nur SNB, Finto oder Staq können die Rotation bestätigen. Die Anbieterseite gibt an, dass die Anmeldedaten rotiert wurden. Ich habe keine Möglichkeit, dies unabhängig zu überprüfen.

Zeitplan

Datum (2026)

Ereignis

  1. Aug

APK analysiert, Fund lokal bestätigt

  1. Aug

Bericht per E-Mail gesendet; Haseen antwortet, dass das Portal auf Benutzer innerhalb Saudi-Arabiens beschränkt ist

  1. Aug

Wiederholte Versuche, gleiche Antwort. Ich twittere darüber; ~1,5 Mio. Aufrufe

  1. Sep

Haseen eröffnet den Thread unaufgefordert neu, bittet um Details. Details gesendet

  1. Sep

Bericht auch bei CERT/CC VINCE (VRF#26-08-DXMKL) als Vermittler eingereicht

  1. Sep

Version 17.5.0 im Google Play Store veröffentlicht

  1. Sep

Erneuter Test des unveränderten 17.5.0 APK-Sets bestätigt vollständige Entfernung

  1. Sep

Dieser Bericht

Ich kann nicht beweisen, dass das Update durch meinen Bericht verursacht wurde. 17.5.0 war möglicherweise bereits in der Pipeline. Was ich zeigen kann, ist, dass das Material in 17.4.9 vorhanden war und in 17.5.0 nicht mehr.

Was ich daraus mitnehmen würde

  1. Etwas in der App zu verstecken, ist keine Sicherheitsgrenze. Nicht in Ressourcen, nicht in einer nativen .so-Datei, nicht hinter Verschleierung oder einem Passwort, das in derselben Binärdatei gespeichert ist. Wenn die App es lesen kann, kann es auch jeder, der die App installiert. Mehrere Teams lernen das jedes Jahr auf öffentliche Weise.
  2. Ein gemeinsames Client-Zertifikat ist keine Authentifizierung. Wenn zehn Millionen Geräte dasselbe Zertifikat vorlegen, sagt es, welche App aufruft, und nichts darüber, wer aufruft, und die App ist eine Datei, die jeder herunterladen kann. Anmeldedaten für eine Drittanbieter-API, insbesondere die einer Bank, gehören auf einen Server, den Sie kontrollieren.
  3. Die geografische Sperrung Ihres Kanals zur Offenlegung von Schwachstellen ist selbst eine Schwachstelle. Angreifer füllen keine Formulare aus. Wenn der einzige Weg, einen Fehler in einer App zu melden, die weltweit an zehn Millionen Menschen veröffentlicht wird, darin besteht, sich physisch in einem Land zu befinden, dann sind die Leute, die das Formular nicht erreichen können, genau die Leute, von denen Sie am meisten hören möchten. Es brauchte einen viralen Tweet, um einen Kanal zu öffnen, den eine security.txt-Datei kostenlos geöffnet hätte.

Ich analysierte das öffentlich verfügbare Play Store APK-Set im Gastmodus, ohne Konto und ohne echte persönliche Daten. Ich unternahm keinen Versuch, die netzwerkseitige Sperre vor der Banking-API zu umgehen. Jeder Wert in diesem Artikel ist entweder strukturell (Pfade, Klassennamen, Zertifikatsmetadaten) oder redigiert; es werden keine privaten Schlüsselmaterialien und keine vollständigen Geheimnisse veröffentlicht.

Mit einem Klick speichern

Virale Artikel mit YouMind per KI tief lesen

Speichere die Quelle, stelle gezielte Fragen, fasse die Argumentation zusammen und verwandle einen viralen Artikel in wiederverwendbare Notizen in einem einzigen KI-Arbeitsbereich.

YouMind entdecken
Für Creator

Verwandle dein Markdown in einen sauberen 𝕏-Artikel

Wenn du eigene Langtexte veröffentlichst, wird die 𝕏-Formatierung von Bildern, Tabellen und Codeblöcken mühsam. YouMind macht aus einem ganzen Markdown-Entwurf einen sauberen, sofort postbaren 𝕏-Artikel.

Markdown zu 𝕏 testen

Mehr Muster zum Entschlüsseln

Aktuelle virale Artikel

Mehr virale Artikel entdecken