Categoria OWASP: MASVS-NETWORK: Network Communication
Panoramica
DownloadManager è un servizio di sistema introdotto nel livello API 9. Gestisce i download HTTP di lunga durata e consente alle applicazioni di scaricare file come attività in background. La sua API gestisce le interazioni HTTP e riprova i download dopo i fallimenti o in caso di modifiche alla connettività e riavvii del sistema.
DownloadManager presenta punti deboli in termini di sicurezza che lo rendono una scelta non sicura per la gestione dei download nelle applicazioni per Android.
(1) CVE in Download Provider
Nel 2018 sono state trovate e corrette tre CVE in DownloadProvider. Di seguito è riportato un riepilogo di ciascuna (vedi dettagli tecnici).
- Bypass delle autorizzazioni del provider di download: senza autorizzazioni concesse, un'app dannosa potrebbe recuperare tutte le voci dal provider di download, che potrebbero includere informazioni potenzialmente sensibili come nomi di file, descrizioni, titoli, percorsi, URL, nonché autorizzazioni di lettura/scrittura complete per tutti i file scaricati. Un'app dannosa potrebbe essere eseguita in background, monitorando tutti i download e divulgando i contenuti da remoto oppure modificando i file all'istante prima che il richiedente legittimo acceda. Ciò potrebbe causare un attacco di denial-of-service per l'utente per le applicazioni di base, inclusa l'impossibilità di scaricare gli aggiornamenti.
- Download Provider SQL injection: tramite una vulnerabilità SQL injection, un'applicazione dannosa senza autorizzazioni potrebbe recuperare tutte le voci dal provider di download. Inoltre, le applicazioni con autorizzazioni limitate, come
android.permission.INTERNET
, potrebbero anche accedere a tutti i contenuti del database da un URI diverso. Potrebbero essere recuperate informazioni potenzialmente sensibili come nomi, descrizioni, titoli, percorsi e URL dei file e, a seconda delle autorizzazioni, potrebbe essere possibile anche l'accesso ai contenuti scaricati. - Informazioni sulle intestazioni delle richieste dei provider di download: un'applicazione
dannosa con l'autorizzazione
android.permission.INTERNET
concessa potrebbe recuperare tutte le voci della tabella delle intestazioni delle richieste del provider di download. Queste intestazioni potrebbero includere informazioni sensibili, come cookie di sessione o intestazioni di autenticazione, per qualsiasi download avviato dal browser Android o da Google Chrome, tra le altre applicazioni. Ciò potrebbe consentire a un malintenzionato di assumere il ruolo dell'utente su qualsiasi piattaforma da cui sono stati ottenuti dati utente sensibili.
(2) Autorizzazioni pericolose
DownloadManager nei livelli API precedenti alla 29 richiede autorizzazioni pericolose
android.permission.WRITE_EXTERNAL_STORAGE
. Per il livello API 29
e versioni successive, le autorizzazioni android.permission.WRITE_EXTERNAL_STORAGE
non sono necessarie, ma l'URI deve fare riferimento a un percorso all'interno delle
directory di proprietà dell'applicazione o a un percorso all'interno della directory di primo livello "Download".
(3) Affidamento su Uri.parse()
DownloadManager si basa sul metodo Uri.parse()
per analizzare la posizione del download richiesto. Nell'interesse del rendimento, la classe Uri
applica poca o nessuna convalida agli input non attendibili.
Impatto
L'utilizzo di DownloadManager potrebbe comportare vulnerabilità tramite lo sfruttamento delle autorizzazioni di scrittura per lo spazio di archiviazione esterno. Poiché le autorizzazioni android.permission.WRITE_EXTERNAL_STORAGE consentono un accesso ampio allo spazio di archiviazione esterno, è possibile che un malintenzionato modifichi silenziosamente file e download, installi app potenzialmente dannose, neghi il servizio alle app di base o causi arresti anomali delle app. I malintenzionati potrebbero anche manipolare ciò che viene inviato a Uri.parse() per indurre l'utente a scaricare un file dannoso.
Mitigazioni
Anziché utilizzare DownloadManager, configura i download direttamente nella tua app utilizzando un client HTTP (ad esempio Cronet), un programmatore/gestore dei processi e un modo per garantire i tentativi di nuovo se si verifica una perdita di rete. La documentazione della libreria include un link a un'app di esempio, nonché istruzioni su come implementarla.
Se la tua applicazione richiede la possibilità di gestire la pianificazione dei processi, eseguire i download in background o riprovare a stabilire il download dopo la perdita di rete, valuta la possibilità di includere WorkManager
e
ForegroundServices
.
Di seguito è riportato il codice di esempio per la configurazione di un download utilizzando Cronet, tratto dal codelab di Cronet.
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();
});
}
Risorse
- Pagina di documentazione principale per DownloadManager
- Report per le CVE di DownloadManager
- Bypass delle autorizzazioni Android CVE 2018-9468
- SQL injection CVE-2018-9493 del provider di download Android
- Bypass delle autorizzazioni del fornitore di download di Android (CVE-2018-9468)
- Pagina di documentazione principale per Cronet
- Istruzioni per l'utilizzo di Cronet in un'applicazione
- Implementazione di esempio di Cronet
- Documentazione per Uri
- Documentazione per ForegroundService
- Documentazione di WorkManager