OWASP kategorisi: MASVS-NETWORK: Ağ İletişimi
Genel Bakış
DownloadManager, API düzeyi 9'da kullanıma sunulan bir sisteme ait hizmettir. Uzun süren HTTP indirmelerini işler ve uygulamaların dosyaları arka plan görevi olarak indirmesine olanak tanır. API'si, HTTP etkileşimlerini yönetir ve indirme işlemlerini hatalardan sonra veya bağlantı değişiklikleri ve sistem yeniden başlatmaları sırasında yeniden dener.
DownloadManager, Android uygulamalarındaki indirmeleri yönetmek için güvenli olmayan bir seçenek haline getiren, güvenlikle ilgili zayıf noktalara sahiptir.
(1) İndirme Sağlayıcı'daki CVE'ler
2018'de DownloadProvider'da üç CVE bulundu ve düzeltildi. Her birinin özeti aşağıda verilmiştir (teknik ayrıntılar için ilgili bölüme bakın).
- İndirme Sağlayıcı İzni Atlama: İzin verilmeyen kötü amaçlı bir uygulama, İndirme Sağlayıcı'daki tüm girişleri alabilir. Bu girişler, dosya adları, açıklamalar, başlıklar, yollar, URL'ler gibi hassas olabilecek bilgilerin yanı sıra indirilen tüm dosyalar için tam OKUMA/YAZMA izinlerini içerebilir. Kötü amaçlı bir uygulama arka planda çalışarak tüm indirmeleri izleyebilir ve içeriklerini uzaktan sızdırabilir veya dosyaları, meşru istekte bulunan kişi erişmeden önce anında değiştirebilir. Bu durum, kullanıcının temel uygulamalarda hizmet reddiyle karşılaşmasına (ör. güncellemeleri indirememesine) neden olabilir.
- Download Provider SQL Yerleştirme: SQL yerleştirme güvenlik açığı sayesinde, izinleri olmayan kötü amaçlı bir uygulama, Download Provider'daki tüm girişleri alabilir. Ayrıca,
android.permission.INTERNETgibi sınırlı izinlere sahip uygulamalar da farklı bir URI'den tüm veritabanı içeriklerine erişebilir. Dosya adları, açıklamalar, başlıklar, yollar, URL'ler gibi hassas bilgiler alınabilir ve izinlere bağlı olarak indirilen içeriklere erişim de mümkün olabilir. - Download Provider Request Headers Information Disclosure:
android.permission.INTERNETizni verilmiş kötü amaçlı bir uygulama, Download Provider istek başlıkları tablosundaki tüm girişleri alabilir. Bu başlıklar, diğer uygulamaların yanı sıra Android Tarayıcı veya Google Chrome'dan başlatılan tüm indirmeler için oturum çerezleri ya da kimlik doğrulama başlıkları gibi hassas bilgiler içerebilir. Bu durum, saldırganın hassas kullanıcı verilerinin elde edildiği herhangi bir platformda kullanıcının kimliğine bürünmesine olanak tanıyabilir.
(2) Tehlikeli İzinler
API düzeyi 29'dan düşük olan sürümlerdeki DownloadManager, tehlikeli izinler gerektirir: android.permission.WRITE_EXTERNAL_STORAGE. API düzeyi 29 ve sonraki sürümlerde android.permission.WRITE_EXTERNAL_STORAGE izinleri gerekli değildir ancak URI, uygulamanın sahip olduğu dizinlerdeki bir yolu veya üst düzey "Downloads" dizinindeki bir yolu referans vermelidir.
(3) Uri.parse() güvenme
DownloadManager, istenen indirmenin konumunu ayrıştırmak için Uri.parse() yöntemini kullanır. Performans açısından Uri sınıfı, güvenilmeyen girişlerde çok az doğrulama uygular veya hiç doğrulama uygulamaz.
Etki
DownloadManager'ın kullanılması, harici depolama alanına YAZMA izinlerinin kötüye kullanılması yoluyla güvenlik açıklarına yol açabilir. android.permission.WRITE_EXTERNAL_STORAGE izinleri, harici depolamaya geniş kapsamlı erişime izin verdiğinden saldırganlar dosyaları ve indirmeleri sessizce değiştirebilir, kötü amaçlı olabilecek uygulamaları yükleyebilir, temel uygulamalara hizmet erişimini engelleyebilir veya uygulamaların kilitlenmesine neden olabilir. Kötü niyetli kişiler, kullanıcının zararlı bir dosya indirmesine neden olmak için Uri.parse() işlevine gönderilenleri de manipüle edebilir.
Çözümler
DownloadManager'ı kullanmak yerine, HTTP istemcisi (ör. Cronet), işlem planlayıcı/yönetici ve ağ kaybı olması durumunda yeniden denemeleri sağlayacak bir yöntem kullanarak indirmeleri doğrudan uygulamanızda ayarlayın. Kitaplığın dokümanlarında, örnek bir uygulamanın bağlantısının yanı sıra nasıl uygulanacağına dair talimatlar da yer alır.
Uygulamanızın işlem planlamasını yönetme, indirme işlemlerini arka planda çalıştırma veya ağ bağlantısı kaybolduktan sonra indirme işlemini yeniden başlatma gibi özelliklere ihtiyacı varsa WorkManager ve ForegroundServices izinlerini eklemeyi düşünebilirsiniz.
Cronet kullanarak indirme ayarlamaya ilişkin örnek kod, Cronet codelab'inden alınmıştır ve aşağıda verilmiştir.
Kotlin
override suspend fun downloadImage(url: String): ImageDownloaderResult {
val startNanoTime = System.nanoTime()
return suspendCoroutine {
cont ->
val request = engine.newUrlRequestBuilder(url, object: ReadToMemoryCronetCallback() {
override fun onSucceeded(
request: UrlRequest,
info: UrlResponseInfo,
bodyBytes: ByteArray) {
cont.resume(ImageDownloaderResult(
successful = true,
blob = bodyBytes,
latency = Duration.ofNanos(System.nanoTime() - startNanoTime),
wasCached = info.wasCached(),
downloaderRef = this@CronetImageDownloader))
}
override fun onFailed(
request: UrlRequest,
info: UrlResponseInfo,
error: CronetException
) {
Log.w(LOGGER_TAG, "Cronet download failed!", error)
cont.resume(ImageDownloaderResult(
successful = false,
blob = ByteArray(0),
latency = Duration.ZERO,
wasCached = info.wasCached(),
downloaderRef = this@CronetImageDownloader))
}
}, executor)
request.build().start()
}
}
Java
@Override
public CompletableFuture<ImageDownloaderResult> downloadImage(String url) {
long startNanoTime = System.nanoTime();
return CompletableFuture.supplyAsync(() -> {
UrlRequest.Builder requestBuilder = engine.newUrlRequestBuilder(url, new ReadToMemoryCronetCallback() {
@Override
public void onSucceeded(UrlRequest request, UrlResponseInfo info, byte[] bodyBytes) {
return ImageDownloaderResult.builder()
.successful(true)
.blob(bodyBytes)
.latency(Duration.ofNanos(System.nanoTime() - startNanoTime))
.wasCached(info.wasCached())
.downloaderRef(CronetImageDownloader.this)
.build();
}
@Override
public void onFailed(UrlRequest request, UrlResponseInfo info, CronetException error) {
Log.w(LOGGER_TAG, "Cronet download failed!", error);
return ImageDownloaderResult.builder()
.successful(false)
.blob(new byte[0])
.latency(Duration.ZERO)
.wasCached(info.wasCached())
.downloaderRef(CronetImageDownloader.this)
.build();
}
}, executor);
UrlRequest urlRequest = requestBuilder.build();
urlRequest.start();
return urlRequest.getResult();
});
}
Kaynaklar
- DownloadManager'ın ana doküman sayfası
- DownloadManager CVE'leri için rapor
- Android İzinini Atlama CVE 2018-9468
- Android Download Provider SQL Injection CVE-2018- 9493
- Android Download Provider Permission Bypass CVE2018-9468
- Cronet'in ana doküman sayfası
- Cronet'i uygulamada kullanma talimatları
- Örnek Cronet uygulaması
- Uri ile ilgili belgeler
- ForegroundService ile ilgili belgeler
- WorkManager ile ilgili belgeler