Catégorie OWASP : MASVS-NETWORK : communication réseau
Présentation
DownloadManager est un service système introduit dans le niveau d'API 9. Il gère les téléchargements HTTP de longue durée et permet aux applications de télécharger des fichiers en tant que tâche en arrière-plan. Son API gère les interactions HTTP et retente les téléchargements en cas d'échec ou de changement de connectivité, et après un redémarrage du système.
DownloadManager présente des faiblesses importantes en termes de sécurité, ce qui en fait un choix non sécurisé pour la gestion des téléchargements dans les applications Android.
(1) CVE dans le fournisseur de téléchargement
En 2018, trois CVE ont été détectées et corrigées dans DownloadProvider. Vous trouverez ci-dessous un résumé de chacun d'eux (voir Informations techniques).
- Contournement de l'autorisation du fournisseur de téléchargement : sans autorisation accordée, une application malveillante peut récupérer toutes les entrées du fournisseur de téléchargement, qui peuvent inclure des informations potentiellement sensibles telles que les noms de fichiers, les descriptions, les titres, les chemins d'accès, les URL, ainsi que des autorisations complètes de LECTURE/ÉCRITURE pour tous les fichiers téléchargés. Une application malveillante peut s'exécuter en arrière-plan, surveiller tous les téléchargements et divulguer leur contenu à distance, ou modifier les fichiers à la volée avant qu'ils ne soient consultés par le demandeur légitime. Cela peut entraîner un déni de service pour l'utilisateur pour les applications principales, y compris l'impossibilité de télécharger des mises à jour.
- Injection SQL du fournisseur de téléchargement : une application malveillante sans autorisation peut récupérer toutes les entrées du fournisseur de téléchargement via une faille d'injection SQL. De plus, les applications disposant d'autorisations limitées, telles que
android.permission.INTERNET, peuvent également accéder à l'ensemble du contenu de la base de données à partir d'un autre URI. Des informations potentiellement sensibles telles que les noms de fichiers, les descriptions, les titres, les chemins d'accès et les URL peuvent être récupérées. Selon les autorisations, l'accès au contenu téléchargé peut également être possible. - Divulgation d'informations sur les en-têtes de requête du fournisseur de téléchargement : une application malveillante disposant de l'autorisation
android.permission.INTERNETaccordée peut récupérer toutes les entrées du tableau des en-têtes de requête du fournisseur de téléchargement. Ces en-têtes peuvent inclure des informations sensibles, telles que des cookies de session ou des en-têtes d'authentification, pour tout téléchargement lancé depuis le navigateur Android ou Google Chrome, entre autres applications. Cela pourrait permettre à un pirate informatique d'usurper l'identité de l'utilisateur sur n'importe quelle plate-forme à partir de laquelle des données utilisateur sensibles ont été obtenues.
(2) Autorisations dangereuses
DownloadManager dans les niveaux d'API inférieurs à 29 nécessite des autorisations dangereuses : android.permission.WRITE_EXTERNAL_STORAGE. Pour le niveau d'API 29 et les niveaux supérieurs, les autorisations android.permission.WRITE_EXTERNAL_STORAGE ne sont pas requises, mais l'URI doit faire référence à un chemin d'accès dans les répertoires appartenant à l'application ou à un chemin d'accès dans le répertoire "Téléchargements" de premier niveau.
(3) Confiance dans Uri.parse()
DownloadManager s'appuie sur la méthode Uri.parse() pour analyser l'emplacement du téléchargement demandé. Pour des raisons de performances, la classe Uri n'applique que peu ou pas de validation sur les entrées non fiables.
Impact
L'utilisation de DownloadManager peut entraîner des failles de sécurité en exploitant les autorisations d'écriture sur le stockage externe. Étant donné que les autorisations android.permission.WRITE_EXTERNAL_STORAGE permettent un accès étendu au stockage externe, il est possible pour un pirate informatique de modifier silencieusement des fichiers et des téléchargements, d'installer des applications potentiellement malveillantes, de refuser l'accès à des applications principales ou de provoquer le plantage d'applications. Des personnes malveillantes pourraient également manipuler ce qui est envoyé à Uri.parse() pour inciter l'utilisateur à télécharger un fichier dangereux.
Stratégies d'atténuation
Au lieu d'utiliser DownloadManager, configurez les téléchargements directement dans votre application à l'aide d'un client HTTP (tel que Cronet), d'un planificateur/gestionnaire de processus et d'un moyen d'assurer les nouvelles tentatives en cas de perte de réseau. La documentation de la bibliothèque inclut un lien vers un exemple d'application ainsi que des instructions sur la façon de l'implémenter.
Si votre application doit gérer la planification des processus, exécuter des téléchargements en arrière-plan ou réessayer d'établir le téléchargement après une perte de réseau, envisagez d'inclure WorkManager et ForegroundServices.
Vous trouverez ci-dessous un exemple de code pour configurer un téléchargement à l'aide de Cronet, tiré de l'atelier de programmation 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();
});
}
Ressources
- Page de documentation principale pour DownloadManager
- Rapport sur les CVE de DownloadManager
- Contournement d'autorisation Android CVE-2018-9468
- Injection SQL du fournisseur de téléchargement Android CVE-2018-9493
- Contournement de l'autorisation du fournisseur de téléchargement Android CVE2018-9468
- Page de documentation principale de Cronet
- Instructions pour utiliser Cronet dans une application
- Exemple d'implémentation de Cronet
- Documentation pour Uri
- Documentation pour ForegroundService
- Documentation pour WorkManager