Категория OWASP: MASVS-NETWORK: Network Communication
Обзор
DownloadManager – это системный сервис, представленный в API уровня 9. Он обрабатывает длительные скачивания по протоколу HTTP и позволяет приложениям скачивать файлы в фоновом режиме. Его API обрабатывает HTTP-взаимодействия и повторяет скачивание после сбоев, изменений в подключении и перезагрузок системы.
DownloadManager имеет уязвимости, связанные с безопасностью, которые делают его небезопасным выбором для управления скачиваниями в приложениях для Android.
1) CVE в Download Provider
В 2018 году в поставщике скачивания было обнаружено и исправлено три уязвимости. Ниже приведено краткое описание каждого из них (см. технические подробности).
- Обход разрешений поставщика скачивания. Вредоносное приложение без разрешений может получить все записи от поставщика скачивания, включая потенциально конфиденциальную информацию, например названия файлов, описания, заголовки, пути и URL, а также полные разрешения на чтение и запись для всех скачанных файлов. Вредоносное приложение может работать в фоновом режиме, отслеживая все скачивания и удаленно передавая их содержимое или изменяя файлы на лету, прежде чем к ним получит доступ законный запрашивающий. Это может привести к отказу в обслуживании для пользователя в основных приложениях, включая невозможность скачивания обновлений.
- Внедрение SQL-кода в Download Provider. Уязвимость позволяет вредоносному приложению без разрешений получить все записи из Download Provider. Кроме того, приложения с ограниченными разрешениями, такие как
android.permission.INTERNET, также могли получить доступ ко всему содержимому базы данных из другого URI. Потенциально конфиденциальная информация, такая как названия файлов, описания, заголовки, пути и URL, может быть извлечена, и, в зависимости от разрешений, также может быть возможен доступ к скачанному контенту. - Раскрытие информации о заголовках запросов поставщика скачивания. Вредоносное приложение с разрешением
android.permission.INTERNETможет получить все записи из таблицы заголовков запросов поставщика скачивания. Эти заголовки могут содержать конфиденциальную информацию, например файлы cookie сеанса или заголовки аутентификации, для любого скачивания, запущенного из браузера Android, Google Chrome или других приложений. Это может позволить злоумышленнику выдать себя за пользователя на любой платформе, с которой были получены конфиденциальные пользовательские данные.
2. Опасные разрешения
Для DownloadManager на уровнях API ниже 29 требуются опасные разрешения: android.permission.WRITE_EXTERNAL_STORAGE. Для уровня API 29 и выше разрешения android.permission.WRITE_EXTERNAL_STORAGE не требуются, но URI должен указывать на путь в каталогах, принадлежащих приложению, или на путь в каталоге верхнего уровня "Скачанные файлы".
3. На что опирается Uri.parse()
DownloadManager использует метод Uri.parse(), чтобы определить местоположение запрашиваемого скачивания. В целях повышения производительности класс Uri практически не проверяет ненадежные входные данные.
Влияние
Использование DownloadManager может привести к уязвимостям, связанным с разрешениями на запись во внешнее хранилище. Разрешение android.permission.WRITE_EXTERNAL_STORAGE предоставляет широкий доступ к внешнему хранилищу, поэтому злоумышленник может незаметно изменять файлы и скачанные данные, устанавливать потенциально вредоносные приложения, блокировать работу основных приложений или вызывать их сбои. Злоумышленники также могут манипулировать данными, отправляемыми в Uri.parse(), чтобы заставить пользователя скачать вредоносный файл.
Предотвращение атак
Вместо DownloadManager настройте скачивание непосредственно в приложении, используя HTTP-клиент (например, Cronet), планировщик или менеджер процессов, а также способ повторной попытки в случае потери сети. В документации библиотеки есть ссылка на пример приложения и инструкции по его реализации.
Если вашему приложению требуется возможность управлять планированием процессов, запускать скачивание в фоновом режиме или повторно устанавливать скачивание после потери сети, рассмотрите возможность включения WorkManager и ForegroundServices.
Ниже приведен пример кода для настройки скачивания с помощью Cronet, взятый из практической работы по 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();
});
}
Ресурсы
- Основная страница документации по DownloadManager
- Отчет об уязвимостях DownloadManager CVE
- Обход разрешений Android (CVE-2018-9468)
- Android Download Provider SQL Injection CVE-2018- 9493
- Обход разрешений поставщика скачивания в Android (CVE-2018-9468)
- Основная страница документации по Cronet
- Инструкции по использованию Cronet в приложении
- Пример реализации Cronet
- Документация для Uri
- Документация по ForegroundService
- Документация по WorkManager