Kategoria OWASP: MASVS-NETWORK: komunikacja sieciowa
Omówienie
DownloadManager to usługa systemowa wprowadzona w interfejsie API poziomu 9. Obsługuje długotrwałe pobieranie przez HTTP i pozwala aplikacjom pobierać pliki jako zadania w tle. Jego interfejs API obsługuje interakcje HTTP i ponawia próby pobrania w przypadku awarii, zmian połączenia i ponownego uruchomienia systemu.
DownloadManager ma luki w zabezpieczeniach związane z bezpieczeństwem, przez co nie jest to bezpieczne rozwiązanie do zarządzania pobieraniem w aplikacjach na Androida.
(1) CVE w dostawcy pobierania
W 2018 r. w DownloadProvider znaleziono 3 luki CVE, które zostały załatane. Poniżej znajdziesz podsumowanie każdego z nich (patrz szczegóły techniczne).
- Pominięcie uprawnień dostawcy – bez przyznanych uprawnień złośliwa aplikacja może pobrać wszystkie wpisy od dostawcy pobierania, które mogą zawierać potencjalnie poufne informacje, takie jak nazwy plików, opisy, tytuły, ścieżki, adresy URL, a także pełne uprawnienia do ODCZYTU/ZAPISU wszystkich pobranych plików. Złośliwa aplikacja może działać w tle, monitorując wszystkie pliki do pobrania i zdalnie udostępniając ich zawartość lub modyfikując je na bieżąco, zanim uzyska do nich dostęp żądający użytkownik. Może to spowodować odmowę usługi dla użytkownika w przypadku podstawowych aplikacji, w tym brak możliwości pobrania aktualizacji.
- Wstrzyknięcie kodu SQL na poziomie dostawcy pobierania – dzięki luki w zabezpieczeniach umożliwiającej wstrzyknięcie kodu SQL złośliwa aplikacja bez uprawnień może pobrać wszystkie wpisy od dostawcy pobierania. Aplikacje z ograniczonymi uprawnieniami, np.
android.permission.INTERNET
, mogą też uzyskiwać dostęp do wszystkich treści bazy danych za pomocą innego identyfikatora URI. Mogą też zostać pobrane informacje poufne, takie jak nazwy plików, opisy, tytuły, ścieżki i adresy URL. W zależności od uprawnień może być również możliwy dostęp do pobranych treści. - Ujawnienie informacji z nagłówków żądań dostawcy treści do pobrania – złośliwa aplikacja z przyznanym uprawnieniem
android.permission.INTERNET
może pobrać wszystkie wpisy z tabeli nagłówków żądań dostawcy treści do pobrania. Te nagłówki mogą zawierać informacje poufne, takie jak pliki cookie sesji lub nagłówki uwierzytelniania, w przypadku każdego pobierania rozpoczętego w przeglądarce Androida lub przeglądarce Google Chrome. Może to umożliwić atakującemu podszywanie się pod użytkownika na dowolnej platformie, z której pozyskano dane wrażliwe.
(2) Niebezpieczne uprawnienia
Interfejs DownloadManager na poziomie niższym niż 29 wymaga uprawnień niebezpiecznych –android.permission.WRITE_EXTERNAL_STORAGE
. W przypadku interfejsu API na poziomie 29 lub wyższym uprawnienia android.permission.WRITE_EXTERNAL_STORAGE
nie są wymagane, ale identyfikator URI musi wskazywać ścieżkę w katalogach należących do aplikacji lub ścieżkę w katalogu najwyższego poziomu „Pobrane”.
(3) Uzależnienie od Uri.parse()
Menedżer pobierania korzysta z metody Uri.parse()
, aby analizować lokalizację żądanego pliku. Ze względu na wydajność klasa Uri
stosuje niewiele lub wcale nie stosuje weryfikacji w przypadku niesprawdzonych danych wejściowych.
Wpływ
Korzystanie z klasy DownloadManager może prowadzić do luk w zabezpieczeniach, ponieważ umożliwia wykorzystywanie uprawnień do zapisu w pamięci zewnętrznej. Uprawnienia android.permission.WRITE_EXTERNAL_STORAGE dają szeroki dostęp do pamięci zewnętrznej, dlatego atakujący może dyskretnie modyfikować i pobierać pliki, instalować potencjalnie szkodliwe aplikacje, odrzucać usługi oraz powodować awarie aplikacji. Osoby o złośliwych zamiarach mogą też manipulować tym, co jest wysyłane do funkcji Uri.parse(), aby zmusić użytkownika do pobrania szkodliwego pliku.
Środki zaradcze
Zamiast używać DownloadManager, skonfiguruj pobieranie bezpośrednio w aplikacji, korzystając z klienta HTTP (np. Cronet), harmonogramu procesów lub menedżera procesów oraz sposobu zapewnienia ponownych prób w przypadku utraty połączenia z internetem. Dokumentacja biblioteki zawiera link do przykładowej aplikacji oraz instrukcje dotyczące jej implementacji.
Jeśli Twoja aplikacja wymaga możliwości zarządzania harmonogramem procesów, uruchamiania pobierania w tle lub ponownego nawiązywania połączenia po utracie połączenia z internetem, rozważ dodanie WorkManager
i ForegroundServices
.
Oto przykładowy kod konfiguracji pobierania za pomocą Cronet (wycięty w codelab) 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();
});
}
Materiały
- Główna strona dokumentacji usługi DownloadManager
- Raport dotyczący CVE w przypadku DownloadManager
- Omijanie uprawnień w Androidzie – CVE-2018-9468
- Wstrzyknięcie kodu SQL na poziomie komponentu Download Provider w Androidzie (CVE-2018-9493)
- Omijanie uprawnień dostawcy pobierania na Androida z kodem CVE2018-9468
- Główna strona dokumentacji Cronet
- Instrukcje korzystania z Cronet w aplikacji
- Przykładowa implementacja Cronet
- Dokumentacja identyfikatora URI
- Dokumentacja usługi ForegroundService
- Dokumentacja WorkManagera