Bir Suudi hükümet uygulaması, bir bankanın özel anahtarını sızdırdı. Bildirmek için Suudi vatandaşı olmak gerekiyordu.

@iam_zachi
İNGILIZCE08 Eyl 2026
235K
1.5K
95
28
1.1K

TL;DR

Bir güvenlik araştırmacısı, resmi Suudi Nusuk uygulamasının, tek haneli bir şifreyle korunan Suudi Ulusal Bankası'na ait bir RSA özel anahtarını ve OAuth kimlik bilgilerini sızdırdığını keşfetti. Coğrafi kısıtlamalı güvenlik açığı bildirim portalları nedeniyle, hatayı bildirmek için viral bir tweet atmak gerekti.

10M+ yükleme sayısına sahip bir devlet uygulamasının içindeki 2.835 baytlık bir dosya, Suudi Ulusal Bankası için canlı bir istemci sertifikası barındırıyordu. Bununla ilgilenecek birini bulmak ise viral bir tweet gerektirdi.

ÖZET

Resmi Nusuk uygulaması (com.moh.nusukapp, Hac ve Umre Bakanlığı, 10M+ yükleme, Google Play'in "Devlet" rozetini taşıyor), özel bir RSA anahtarı ve Suudi Ulusal Bankası tarafından düzenlenmiş bir istemci sertifikası içeren bir PKCS#12 dosyasıyla birlikte geldi. Bu dosyanın şifresi, uygulamanın kendi kodunda birkaç satır ötede sabit kodlanmıştı. Tek bir karakterdi: 2.

Hemen yanında, düz metin olarak, bankanın Bankacılık-Hizmet-Olarak (BaaS) API'si için OAuth2 istemci kimliği ve istemci sırrı yer alıyordu ve identity accounts cards verification kyc cardpay transfers kapsamlarını talep ediyordu.

Google Play'den uygulamayı indiren herkes tüm bunlara sahipti.

Bunu bildirmeye çalıştım. Bana güvenlik açığı portalının yalnızca Suudi Arabistan içindeki kullanıcılar için erişilebilir olduğu söylendi. Ben de tweet attım. Tweet 1,5 milyon görüntüleme aldı ve aniden aynı portal ayrıntıları istedi. Bir gün sonra kimlik bilgileri uygulamadan kaldırıldı.

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

Bu yazı yalnızca bankacılık bulgusunu kapsamaktadır. Aşağıdaki her şey yayındaki uygulamada düzeltilmiştir ve satıcı tarafı kimlik bilgilerinin döndürüldüğünü belirtmektedir.

Nusuk Nedir?

Nusuk, Suudi hükümetinin Hac ve Umre için resmi platformudur. Hac izinlerini, e-vizeleri, rezervasyonları ve Nusuk Kartı'nı yönetir. Hac ve Umre Bakanlığı tarafından işletilir, Google Play'de doğrulanmış bir devlet uygulaması olarak işaretlenmiştir ve on milyondan fazla yüklemeye sahiptir. Ayrıca Suudi Ulusal Bankası ile birlikte oluşturulan ve Suudi Arabistan merkez bankası SAMA tarafından onaylanan bir cüzdan özelliği olan Nusuk Wallet'ı da içerir. Cüzdan, bu makalenin konusu olan kısımdır.

Bulgular

APK setini doğrudan Google Play'den (sürüm 17.4.9, versionCode 131215) çektim ve paketini açtım. Olağanüstü bir şey yoktu: standart Kotlin/Compose, paketleyici yok, anlamlı bir karartma yok.

Uygulamanın kaynaklarının içinde, res/raw/nusuk.pfx konumunda 2.835 baytlık bir PKCS#12 kapsayıcısı bulunuyordu. Bir .pfx dosyası, bir sertifika ve özel anahtarının bir şifreyle şifrelenmiş standart paket formatıdır.

Şifre, uygulamanın kodunda, dosyanın yüklendiği yerden birkaç satır ötedeydi:

text
1const-string v3, "2"

Ayrıştırılmış bayt kodunda bir değişmez değer olarak duran tek bir karakter. Bir openssl çağrısı sonrası:

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

Bir banka tarafından düzenlenmiş, bir yıl daha geçerli, bir istemciyi bir sunucuya TLS üzerinden doğrulamak için tasarlanmış canlı bir istemci sertifikası.

Yalnız değildi. Aynı kod yolu, com.walletstaq olarak paketlenmiş ve SNB'nin Finto BaaS platformunu yürüten Staq Technologies'in Trustless SDK'sına bağlı olan cüzdan bileşenidir. Orada düz metin olarak üç değer daha duruyordu:

istenen OAuth kapsamı ile birlikte:

text
1identity accounts cards verification kyc cardpay transfers

Her iki kimlik bilgisi de tek bir dizede birleştirilmiş ve temel URL ile birlikte bir hata ayıklama günlükçüsüne verilmişti.

(Anahtar malzemeyi veya tam gizli değerleri yayınlamıyorum. Burada önemli olan sorunun şeklidir.)

Bunun Neden Kötü Olduğu, Açık Bir Dille

Banka API'sini iki kilitli bir kapı olarak düşünün.

İlk kilit karşılıklı TLS'dir (mTLS). Normalde bir sunucu, kimliğini size bir sertifikayla kanıtlar. mTLS ile siz de kimliğinizi sunucuya bir sertifikayla kanıtlamak zorundasınız. İşte bu .pfx dosyasıdır: sertifika ve ona sahip olduğunuzu kanıtlayan özel anahtar. Bunun yalnızca meşru istemci sisteminde bulunması gereken şey olduğu varsayılır.

İkinci kilit, uygulamanın bankanın API'sinden bir erişim jetonu istemek için kullandığı şifre olan OAuth istemci sırrıdır.

Her iki kilit de Google Play'deki ücretsiz bir uygulamanın içinde gönderildi ve ilkini tutan kutunun anahtarı 2 rakamıydı.

Hedef ana bilgisayarın gerçekten mTLS'yi zorunlu kıldığını doğruladım: api.baas.alahli.com ile TLS anlaşması istemci sertifikaları talep ediyor (Kabul Edilebilir istemci sertifikası CA adlarını gönderiyor) ve sunucu sertifikası CN=*.baas.alahli.com, O=The Saudi National Bank olarak okunuyor. Yani bu, bir test ortamı için dekoratif bir sertifika değildi. Kimlik, hesaplar, kartlar, KYC, kart ödemeleri ve transferleri kapsamlarını kapsayan bir üretim bankacılık API'sinin ön kapısının kimlik bilgisiydi.

Sızıntıdan ayrı olarak, altında yatan bir tasarım sorunu var. On milyon cihaza aynı şekilde gönderilen bir istemci sertifikası, bir kurulumu diğerinden ayırt edemez. Her kopya aynı kimlik bilgisini sunar, bu nedenle sertifika bankaya hangi uygulamanın aradığını söyler, kimin aradığı hakkında hiçbir şey söylemez. Bunun gibi bir API için kimlik bilgileri kendi arka uç sisteminizin arkasında olmalıdır: uygulama sizin sunucunuzla, sizin sunucunuz da bankayla konuşur.

Yapmadığım Şeyler

Kimlik bilgilerinin canlı olup olmadığını görmek için jeton bitiş noktasına (POST /api/tppa/token) karşı tam olarak bir kontrol çalıştırdım. nginx'ten HTTP 403 döndürdü. Hiç sertifika olmadan yapılan bir istek de aynı şekilde yanıt verdi ve çıplak kök URL de öyle. Bu, API'nin önünde duran ve neredeyse kesinlikle coğrafi olan ağ düzeyinde bir engeldir ve kimlik bilgilerinin çalışıp çalışmadığı hakkında hiçbir şey söylemez.

Suudi Arabistan dışından bu kimlik bilgilerinin aktif olup olmadığını belirleyemedim. Orada durdum. Daha fazlası, bir bankanın erişim kontrolünü aşma girişimi olurdu ve bulgu buna bağlı değildir: herkese açık olarak indirilebilir bir yapıtta bulunan özel bir anahtar ve transfer kapsamına sahip bir bankacılık OAuth sırrı, şahsen uç noktaya ulaşabilsem de ulaşamasam da bulgudur.

Bildirmeye Çalışmak

Tweet'in viral olmasına neden olan kısım budur ve daha ilginç olan yarısıdır.

Bunu sorumlu bir şekilde bildirmenin bir yolunu aradım. Var olanlar:

Kanal

Sonuç

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

Mevcut değil

Suudi CERT (cert.gov.sa) güvenlik açığı bildirim sayfası

NCA'ya yönlendiriyor; kendi bildirim sayfası kapatılmış

NCA güvenlik açığı formu (haseen.gov.sa)

Almanya'dan erişilemiyor: zaman aşımı, coğrafi engelleme

bugbounty.sa

Kapalı program, dışarıdan HTTP 403

HackerOne / Bugcrowd

Nusuk, Bakanlık veya Elm için program yok

Uygulama mağazası listeleme iletişim bilgileri

Destek adresleri, güvenlik yetkisi yok

Bunların hiçbiri bana güvenlik yetkisi olan ve gerçekten ulaşabileceğim bir kanal bırakmadı. Yine de e-posta gönderdim. Haseen Support'tan gelen yanıt:

"Haseen portalına erişim, Suudi Arabistan Krallığı içindeki kullanıcılarla sınırlıdır. Daha fazla sorunuz için resmi Haseen Portalı'nda bulunan 'Önemsiyoruz' hizmeti aracılığıyla bizimle iletişime geçebilirsiniz."

Ulaşamadığım şey olan Haseen Portalı. Suudi olmadığımı, bunun bir devlet uygulamasında ifşa edilmiş özel bir banka sertifikası olduğunu ve sadece teslim etmek istediğimi açıklayarak cevap yazdım. Cevap yine formun yalnızca KSA vatandaşları için çalıştığıydı.

Bunun üzerine, koordinasyon aracısı olarak CERT/CC'ye VINCE platformları aracılığıyla (VRF#26-08-DXMKL), yalnızca bu bulguya odaklanarak bir rapor gönderdim. Etkilenen tarafın kendine ait ulaşılabilir bir kanalı olmadığında izlenen yol budur.

Ve sonra, çoğunlukla hayal kırıklığından, bunun hakkında tweet attım.

Tweet 1,5 milyon görüntüleme aldı. Saatler içinde, Haseen Support, portalın benim için olmadığını iki kez söylemiş olan aynı e-posta dizisinde, talep edilmeksizin bana e-posta gönderdi:

"İlgili ekibimize göre, lütfen bize güvenlik açığı hakkında daha fazla ayrıntı sağlayın."

Tüm ayrıntıları gönderdim. Süreç hakkında haklı olmaktansa sorunun çözülmesini tercih ederim.

Düzeltme

Bir sonraki uygulama güncellemesi hem Android hem de iOS'ta yayınlandı. Yeni Android yapısını (17.5.0, versionCode 156635) doğrudan Play'den çektim ve analiz ettiğimle karşılaştırdım. Üç kontrol:

  1. Yeni APK setinde hiçbir yerde sertifika kapsayıcısı yok: .pfx, .p12, .pkcs12, .jks, .bks, .pem veya .key uzantılı dosya yok. Ayrıca eski nusuk.pfx'in hash'ini çıkardım ve yeniden adlandırılmış olması ihtimaline karşı yeni yapıdaki aynı boyuttaki her dosyayla bayt bayt karşılaştırdım. Eşleşme yok.
  2. Bilinen kimlik bilgilerinin hiçbiri yok. Temel URL, kapsam, istemci kimliği ve istemci sırrı için eski değerlerin aynısını tüm DEX dosyalarında, yerel kütüphanelerde, varlıklarda, XML, JSON ve ham kaynaklarda aradım. Dördünde de sıfır sonuç.
  3. Kod yolu yok. Sürüm 17.4.9, com.walletstaq ve com.trustless paketleri altında 4.571 dosya içeriyordu; 17.5.0 sıfır içeriyor. baas.alahli, tppa/token, nusuk.pfx, sertifika konusu ve düzenleyen adı işaretleri, kodu çözülmüş yapıda sıfır sonuç veriyor.

Tüm cüzdan ve BaaS entegrasyonu kaldırıldı. 1. adımdaki yeniden adlandırma kontrolü, değerlerin pakette başka bir yere taşınmış olma ihtimalini ortadan kaldırır.

Dışarıdan Kimsenin Doğrulayamayacağı Kısım

Bir uygulamadan bir sırrı kaldırmak, sırrı geçersiz kılmaz. Eski APK kopyaları sonsuza kadar kullanılabilir kalır ve sertifika Nisan 2027'ye kadar geçerliydi. Bu nedenle açık soru, iptal edilip edilmediği ve OAuth sırrının döndürülüp döndürülmediğidir.

Bunu bağımsız olarak kontrol etmenin bir yolunu aradım. Yok ve bunun nedeni de başlı başına bir bulgu.

Sertifika, herhangi bir crlDistributionPoints uzantısı taşımaz, bu nedenle hiçbir iptal listesine atıfta bulunulmaz. Tek iptal uç noktası:

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

.cluster.local, bir Kubernetes kümesinin dahili DNS sonekidir. Tanımı gereği genel internette yönlendirilemez ve bu kümenin dışındaki herhangi bir yerden, düz HTTP üzerinden 8080 numaralı bağlantı noktasında NXDOMAIN olarak çözümlenir.

Dolayısıyla, bu sertifikanın iptal durumu bankanın altyapısı dışından kontrol edilemez, çünkü burada sorgulanacak bir şey yoktur. Bu tek kümenin dışındaki herhangi bir güvenen taraf için, bu düzenleyiciden gelen sertifikalar etkili bir şekilde iptal edilemez; bu da başlı başına birinin mimari incelemesinde bir paragrafı hak eder.

Aynı alan ayrıca, on milyon kişiye dağıtılan bir uygulamada, bir bankanın BaaS platformunun üretim PKI'sının küme adını, ad alanını, hizmet adını ve bağlantı noktasını da yayınladı.

Yalnızca SNB, Finto veya Staq döndürmeyi onaylayabilir. Satıcı tarafı, kimlik bilgilerinin döndürüldüğünü belirtmektedir. Bunu bağımsız olarak doğrulamanın bir yolum yok.

Zaman Çizelgesi

Tarih (2026)

Olay

29 Ağu

APK analiz edildi, bulgu yerel olarak doğrulandı

29 Ağu

Rapor e-postayla gönderildi; Haseen portalın KSA içindeki kullanıcılarla sınırlı olduğunu yanıtladı

31 Ağu

Tekrarlanan girişimler, aynı yanıt. Tweet attım; ~1,5 milyon görüntüleme

1 Eyl

Haseen talep edilmeksizin konuyu yeniden açtı, ayrıntıları istedi. Ayrıntılar gönderildi

1 Eyl

Rapor ayrıca CERT/CC VINCE'e (VRF#26-08-DXMKL) aracı olarak iletildi

1 Eyl

Sürüm 17.5.0 Google Play'de yayınlandı

3 Eyl

Değiştirilmemiş 17.5.0 APK setinde yeniden test, tamamen kaldırıldığını doğruladı

8 Eyl

Bu yazı

Güncellemenin benim raporumdan kaynaklandığını kanıtlayamam. 17.5.0 zaten yolda olabilir. Gösterebileceğim şey, malzemenin 17.4.9'da olduğu ve 17.5.0'da olmadığıdır.

Bundan Çıkarılacak Dersler

  1. Bir şeyi uygulamanın içine gizlemek bir güvenlik sınırı değildir. Kaynaklarda, yerel bir .so dosyasında, karartma veya aynı ikili dosyada saklanan bir şifrenin arkasında da değildir. Uygulama okuyabiliyorsa, uygulamayı yükleyen herkes de okuyabilir. Birçok ekip bunu her yıl kamuoyu önünde öğrenir.
  2. Paylaşılan bir istemci sertifikası kimlik doğrulama değildir. On milyon cihaz aynı sertifikayı sunuyorsa, bu hangi uygulamanın aradığını söyler, kimin aradığı hakkında hiçbir şey söylemez ve uygulama herkesin indirebileceği bir dosyadır. Bir üçüncü taraf API'si için, özellikle de bir bankanınki için kimlik bilgileri, sizin kontrol ettiğiniz bir sunucuya ait olmalıdır.
  3. Güvenlik açığı bildirim kanalınızı coğrafi olarak sınırlamak, başlı başına bir güvenlik açığıdır. Saldırganlar form doldurmaz. Küresel olarak on milyon kişiye yayınlanan bir uygulamadaki bir kusuru bildirmenin tek yolu fiziksel olarak bir ülkenin içinde olmaksa, o zaman forma ulaşamayan insanlar tam olarak en çok duymak istediğiniz kişilerdir. Bir security.txt dosyasının ücretsiz olarak açacağı bir kanalı açmak için viral bir tweet gerekti.

Genel olarak erişilebilen Play Store APK setini misafir modunda, hesap olmadan ve gerçek kişisel veri olmadan analiz ettim. Bankacılık API'sinin önündeki ağ düzeyindeki engeli aşmaya yönelik herhangi bir girişimde bulunmadım. Bu makaledeki her değer ya yapısaldır (yollar, sınıf adları, sertifika meta verileri) ya da sansürlenmiştir; hiçbir özel anahtar malzemesi ve hiçbir tam sır yayınlanmamıştır.

Tek tıkla kaydet

YouMind ile viral makaleleri AI derin okumayla incele

Kaynağı kaydedin, odaklı sorular sorun, argümanı özetleyin ve viral bir makaleyi tek bir AI çalışma alanında yeniden kullanılabilir notlara dönüştürün.

YouMind'ı keşfet
Üreticiler için

Markdown'ınızı temiz bir 𝕏 makalesine dönüştürün

Kendi uzun yazılarınızı yayımlarken görselleri, tabloları ve kod bloklarını 𝕏 için biçimlendirmek zahmetlidir. YouMind, eksiksiz bir Markdown taslağını temiz ve hemen paylaşılabilir bir 𝕏 makalesine dönüştürür.

Markdown'dan 𝕏'e deneyin

Çözülecek daha fazla kalıp

Son viral makaleler

Daha fazla viral makale keşfet