OWASP-Kategorie:MASVS-NETWORK: Network Communication
Übersicht
DownloadManager ist ein Systemdienst, der in API-Ebene 9 eingeführt wurde. Es verarbeitet lang andauernde HTTP-Downloads und ermöglicht Anwendungen, Dateien als Hintergrundaufgabe herunterzuladen. Die API verarbeitet HTTP-Interaktionen und versucht Downloads nach Fehlern, bei Änderungen der Verbindung und nach Systemneustarts noch einmal.
DownloadManager weist sicherheitsrelevante Schwachstellen auf, die ihn zu einer unsicheren Option für die Verwaltung von Downloads in Android-Apps machen.
1. CVEs im Downloadanbieter
2018 wurden im Download-Provider drei CVEs gefunden und gepatcht. Im Folgenden finden Sie eine Zusammenfassung der einzelnen Methoden (siehe Technische Details).
- Umgehung der Berechtigung des Downloadanbieters: Ohne erteilte Berechtigungen könnte eine schädliche App alle Einträge vom Downloadanbieter abrufen, was potenziell vertrauliche Informationen wie Dateinamen, Beschreibungen, Titel, Pfade, URLs sowie vollständige LESE-/SCHREIBberechtigungen für alle heruntergeladenen Dateien umfassen könnte. Eine schädliche App könnte im Hintergrund ausgeführt werden, alle Downloads überwachen und deren Inhalte remote weitergeben oder die Dateien im laufenden Betrieb ändern, bevor der rechtmäßige Anforderer darauf zugreift. Dies kann zu einem Denial-of-Service für den Nutzer bei wichtigen Anwendungen führen, einschließlich der Unfähigkeit, Updates herunterzuladen.
- Download Provider SQL Injection: Über eine SQL-Injection-Sicherheitslücke kann eine schädliche Anwendung ohne Berechtigungen alle Einträge vom Downloadanbieter abrufen. Außerdem könnten Anwendungen mit eingeschränkten Berechtigungen, z. B.
android.permission.INTERNET, auch über einen anderen URI auf alle Datenbankinhalte zugreifen. Potenziell vertrauliche Informationen wie Dateinamen, Beschreibungen, Titel, Pfade und URLs können abgerufen werden. Je nach Berechtigungen ist möglicherweise auch der Zugriff auf heruntergeladene Inhalte möglich. - Offenlegung von Informationen zu Anfrageheadern des Downloadanbieters: Eine schädliche Anwendung mit der Berechtigung
android.permission.INTERNETkönnte alle Einträge aus der Tabelle „Anfrageheader des Downloadanbieters“ abrufen. Diese Header können vertrauliche Informationen wie Sitzungscookies oder Authentifizierungsheader für alle Downloads enthalten, die unter anderem über den Android-Browser oder Google Chrome gestartet wurden. So könnte ein Angreifer den Nutzer auf jeder Plattform imitieren, von der vertrauliche Nutzerdaten stammen.
(2) Gefährliche Berechtigungen
Für DownloadManager in API-Levels unter 29 sind gefährliche Berechtigungen erforderlich: android.permission.WRITE_EXTERNAL_STORAGE. Bei API-Level 29 und höher sind android.permission.WRITE_EXTERNAL_STORAGE-Berechtigungen nicht erforderlich. Der URI muss jedoch auf einen Pfad in den Verzeichnissen verweisen, die der Anwendung gehören, oder auf einen Pfad im Verzeichnis „Downloads“ der obersten Ebene.
(3) Verlass auf Uri.parse()
DownloadManager verwendet die Methode Uri.parse(), um den Speicherort des angeforderten Downloads zu parsen. Aus Leistungsgründen wird in der Uri-Klasse nur wenig oder gar keine Validierung für nicht vertrauenswürdige Eingaben angewendet.
Auswirkungen
Die Verwendung von DownloadManager kann zu Sicherheitslücken führen, wenn WRITE-Berechtigungen für externen Speicher ausgenutzt werden. Da die Berechtigungen android.permission.WRITE_EXTERNAL_STORAGE einen umfassenden Zugriff auf den externen Speicher ermöglichen, kann ein Angreifer Dateien und Downloads unbemerkt ändern, potenziell schädliche Apps installieren, wichtigen Apps den Dienst verweigern oder Apps zum Absturz bringen. Böswillige Akteure könnten auch manipulieren, was an Uri.parse() gesendet wird, um den Nutzer zum Herunterladen einer schädlichen Datei zu veranlassen.
Gegenmaßnahmen
Richten Sie Downloads direkt in Ihrer App ein, anstatt DownloadManager zu verwenden. Dazu benötigen Sie einen HTTP-Client (z. B. Cronet), einen Prozessplaner/-manager und eine Möglichkeit, Wiederholungsversuche bei Netzwerkverlust zu gewährleisten. Die Dokumentation der Bibliothek enthält einen Link zu einer Beispiel-App sowie eine Anleitung zur Implementierung.
Wenn Ihre Anwendung die Möglichkeit zum Verwalten der Prozessplanung, zum Ausführen von Downloads im Hintergrund oder zum erneuten Einrichten des Downloads nach einem Netzwerkverlust erfordert, sollten Sie WorkManager und ForegroundServices einbinden.
Der folgende Beispielcode für das Einrichten eines Downloads mit Cronet stammt aus dem Cronet-Codelab.
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();
});
}
Ressourcen
- Hauptdokumentationsseite für DownloadManager
- Bericht zu CVEs für DownloadManager
- Android Permission Bypass CVE 2018-9468
- Android Download Provider SQL Injection CVE-2018- 9493
- Umgehung der Berechtigung des Android-Downloadanbieters CVE2018-9468
- Hauptdokumentationsseite für Cronet
- Anleitung zur Verwendung von Cronet in einer Anwendung
- Beispiel für die Cronet-Implementierung
- Dokumentation für Uri
- Dokumentation zu ForegroundService
- Dokumentation zu WorkManager