İçeriğe geç
Sedat Özdemir
Yazılar

android-security

Cebimizdeki Casuslar: Mobil Güvenliğin Görünmeyen Yüzü ve Tersine Mühendislik Maceraları

Mobil uygulamalarda 'güvenli' sandığımız o alanların aslında ne kadar kırılgan olduğunu, kendi yaptığım hatalardan yola çıkarak anlatıyorum. Tersine mühendislikten SSL pinning'e kadar sahada karşılaştığımız gerçekleri konuşalım.

Sedat Özdemir
· 4 dk okuma

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:

  1. 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.
  2. 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.
  3. Güvenli Depolama: Android Keystore veya iOS Keychain kullanın. Bu yapılar, anahtarları donanımsal seviyede korumak için tasarlandı.
  4. Bütünlük Kontrolü: Uygulamanız başladığında isRooted() veya isEmulator() kontrollerini yapın. Eğer cihaz güvensizse, kritik işlemleri kısıtlayın.
  5. Network Security Config: Android tarafında network_security_config.xml kullanarak 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!

İlgili yazılar