Yıllar önce, henüz sektöre yeni ısındığım dönemlerde bir mobil uygulama sızma testi projesindeyim. 'Obfuscation' (kod karartma) konusunu o kadar büyütmüşüm ki gözümde, 'Abi ProGuard kullandılarsa hayatta çözemeyiz' diyorum. Uygulamayı bir dezenfekte edip incelemeye başladım. Kendi kendime dedim ki: 'Bu API anahtarlarını kimse bulamaz, zaten Java kodu karmaşıklaşmış.' Ertesi sabah kıdemli bir abi yanıma gelip sadece strings komutunu kullanarak o 'bulunamaz' dediğim tüm key'leri önüme koyduğunda, yerin dibine girmiştim. O gün anladım ki; mobil dünyada 'gizlilik' bir illüzyon, önemli olan tek şey 'direnç'.
Bugün sizinle o günden bu yana öğrendiklerimi, cebimizdeki o akıllı kutuların aslında ne kadar geveze olabileceğini konuşalım. Mobil güvenlik dediğimizde çoğu yazılımcı sadece 'HTTPS kullanıyoruz, bitti' diye düşünüyor. Keşke o kadar kolay olsa dostum.
APK/IPA Sadece Bir Başlangıçtır
Bir mobil uygulamayı telefonuna indirdiğinde, aslında saldırganın eline kocaman bir hediye paketi veriyorsun. Android tarafında APK, iOS tarafında IPA dosyası birer ZIP arşivinden fazlası değil. Saldırgan (ya da bizim gibi Red Team'ciler) bu paketi açtığında ilk baktığımız yer AndroidManifest.xml veya Info.plist dosyalarıdır.
Burada yapılan en büyük hata, gereksiz izinlerin (permissions) havada uçuşması. 'Fener uygulaması neden rehberime erişmek istiyor?' sorusu artık bir klasik oldu ama işin teknik boyutu daha derin. Mesela android:debuggable="true" olarak unutulmuş bir flag, tüm veritabanını dışarıya sızdırmak için açık bir davetiyedir.
Kodun İçindeki Hazine: Tersine Mühendislik
JADX-GUI gibi araçları açıp bir APK'yı içine sürüklediğinde, karşındaki kod her ne kadar a.b.c.v() gibi anlamsız fonksiyon isimlerine sahip olsa da, iş mantığını çözmek bazen sadece 15 dakikamızı alıyor. Yazılımcı arkadaşım, sen o kritik iş mantığını (mesela indirim kuponu hesaplama veya ödeme doğrulama) client tarafında yaparsan, ben o kodu manipüle ederim.
Örnek bir senaryo düşünelim; testCompany isimli bir firmanın sadakat uygulaması olsun. Kodda şöyle bir yapı gördüğümü düşün:
// Zararsızlaştırılmış (Defanged) Örnek Kod
public class DiscountManager {
public boolean isValidCoupon(String code) {
// Hata: Kritik mantık client tarafında!
if (code.equals("BEDAVA100")) {
return true;
}
return false;
}
}
Burada yapacağım tek şey, Frida kullanarak bu fonksiyonun her zaman true dönmesini sağlamak. Telefonu root'lamaya bile gerek kalmayabilir bazen.
Veri Depolamanın Karanlık Köşeleri
'Verileri telefonda şifreli tutuyoruz' cümlesi genellikle bir yalandır. Çoğu zaman SharedPreferences veya iOS tarafındaki UserDefaults dosyalarına baktığımızda, kullanıcıların session token'larını, hatta bazen şifrelerini (evet, hala yapan var) kabak gibi ortada görüyoruz.
Bir Red Team operasyonunda karşılaştığımız tipik bir shared_prefs içeriği şöyle görünebilir:
<!-- /data/data/com.testcompany.app/shared_prefs/UserPrefs.xml -->
<map>
<string name="api_token">eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.example_token_12345</string>
<string name="user_role">admin</string>
<boolean name="is_logged_in" value="true" />
</map>
Burada api_token bilgisini çalan bir malware, kullanıcının ruhu duymadan tüm hesabını ele geçirebilir. Çözüm mü? EncryptedSharedPreferences kullanmak veya hassas hiçbir şeyi (evet, hiçbir şeyi) cihazda kalıcı olarak tutmamak.
Ortadaki Adam (MitM) ve SSL Pinning Yanılgısı
Birçok kişi SSL Pinning kullandığında trafiğin asla dinlenemeyeceğini sanıyor. Ah, tatlı bir yanılgı... Burp Suite'i kurup, telefona sertifikayı yükledikten sonra Frida ile bir script çalıştırıyoruz ve hoop; tüm trafik karşımızda.
İşte basit bir Frida script'inin (zararsızlaştırılmış) çalışma mantığı:
// SSL Pinning Bypass için basit bir mantık taslağı
Java.perform(function () {
var CertificatePinner = Java.use('okhttp3.CertificatePinner');
CertificatePinner.check.overload('java.lang.String', 'java.util.List').implementation = function (str, list) {
// Kontrolü pas geç ve hiçbir şey yapma
console.log('SSL Pinning Check Atlatıldı: ' + str);
return;
};
});
Bu bize şunu gösteriyor: Güvenlik, sadece bir katmana (SSL Pinning gibi) güvenerek sağlanamaz. Uygulama, çalışma zamanında (runtime) kendi bütünlüğünü kontrol etmeli (Root detection, Integrity check vb.).
Peki Ne Yapmalı? (Defensive Mindset)
Siz yazılımcı ve sistem yöneticisi dostlarıma sahadan birkaç tavsiye bırakayım:
- Kod Karartma (Obfuscation) Yetmez: ProGuard veya R8 kullanın ama bunun bir güvenlik önlemi değil, sadece bir 'zorlaştırma' yöntemi olduğunu unutmayın.
- Hassas Veriler: API anahtarlarını, şifreleri veya gizli URL'leri kodun içine (hardcoded) asla gömmeyin. Bunları dinamik olarak güvenli bir vault'tan çekin veya en azından
Native C++(NDK) katmanında tutarak işi bir tık daha zorlaştırın. - Güvenli Depolama: Android Keystore veya iOS Keychain kullanın. Bu yapılar, anahtarları donanımsal seviyede korumak için tasarlandı.
- Bütünlük Kontrolü: Uygulamanız başladığında
isRooted()veyaisEmulator()kontrollerini yapın. Eğer cihaz güvensizse, kritik işlemleri kısıtlayın. - Network Security Config: Android tarafında
network_security_config.xmlkullanarak sadece belirli domain'lere ve sertifikalara izin verin.
Kapanış Düşüncesi
Mobil güvenlik, kedi-fare oyununun en yoğun yaşandığı yerlerden biri. Biz Red Team tarafında her zaman bir açık kapı ararız; sizin göreviniz ise o kapıyı tamamen kilitlemek değil, saldırganın o kapıyı açmak için harcayacağı maliyeti (zaman ve teknik bilgi) artırmaktır. Unutmayın, hiçbir sistem %100 güvenli değildir, sadece 'yeterince' güvenli sistemler vardır.
Bir sonraki teknik analizde, belki de iOS tarafındaki 'Objective-C Runtime' manipülasyonlarına dalarız. O zamana kadar, kodunuz temiz, veritabanınız kapalı olsun!
