Kategoria OWASP: MASVS-NETWORK: Network Communication
Przegląd
DownloadManager to usługa systemowa wprowadzona na poziomie interfejsu API 9. Obsługuje długotrwałe pobieranie plików przez HTTP i umożliwia aplikacjom pobieranie plików jako zadania w tle. Jego interfejs API obsługuje interakcje HTTP i ponawia pobieranie po niepowodzeniach, zmianach połączenia i ponownym uruchomieniu systemu.
Menedżer pobierania ma luki w zabezpieczeniach, które sprawiają, że nie jest bezpiecznym wyborem do zarządzania pobieraniem w aplikacjach na Androida.
(1) Luki w zabezpieczeniach w usłudze Download Provider
W 2018 roku w usłudze DownloadProvider znaleziono i załatano 3 luki w zabezpieczeniach. Poniżej znajdziesz podsumowanie każdego z nich (szczegóły techniczne znajdziesz tutaj).
- Obejście uprawnień dostawcy pobierania – bez przyznanych uprawnień szkodliwa aplikacja może pobrać wszystkie wpisy od dostawcy pobierania, które mogą zawierać potencjalnie informacje poufne, takie jak nazwy plików, opisy, tytuły, ścieżki, adresy URL, a także pełne uprawnienia do odczytu i zapisu wszystkich pobranych plików. Złośliwa aplikacja może działać w tle, monitorując wszystkie pobierane pliki i zdalnie ujawniając ich zawartość lub modyfikując pliki na bieżąco, zanim uzyska do nich dostęp uprawniony użytkownik. Może to spowodować odmowę dostępu do usług dla użytkownika w przypadku podstawowych aplikacji, w tym brak możliwości pobierania aktualizacji.
- Wstrzyknięcie kodu SQL na poziomie komponentu Download Provider – z powodu luki w zabezpieczeniach umożliwiającej wstrzyknięcie kodu SQL złośliwa aplikacja bez uprawnień może pobrać wszystkie wpisy z komponentu Download Provider. Aplikacje z ograniczonymi uprawnieniami, np.
android.permission.INTERNET, mogą też uzyskiwać dostęp do całej zawartości bazy danych z innego identyfikatora URI. Można uzyskać dostęp do potencjalnie poufnych informacji, takich jak nazwy plików, opisy, tytuły, ścieżki i adresy URL, a w zależności od uprawnień także do pobranych treści. - Ujawnianie informacji o nagłówkach żądań dostawcy pobierania – złośliwa aplikacja z przyznanym uprawnieniem
android.permission.INTERNETmoże pobrać wszystkie wpisy z tabeli nagłówków żądań dostawcy pobierania. 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 na Androida lub Google Chrome, a także w innych aplikacjach. Może to umożliwić atakującemu podszywanie się pod użytkownika na dowolnej platformie, z której uzyskano dane użytkownika wrażliwe.
(2) Niebezpieczne uprawnienia
Menedżer pobierania na poziomach interfejsu API niższych niż 29 wymaga uprawnień niebezpiecznych –android.permission.WRITE_EXTERNAL_STORAGE. W przypadku interfejsu API na poziomie 29 i wyższym android.permission.WRITE_EXTERNAL_STORAGE
nie są wymagane uprawnienia, ale identyfikator URI musi odnosić się do ścieżki w katalogach należących do aplikacji lub ścieżki w katalogu najwyższego poziomu „Pobrane”.
(3) Reliance on Uri.parse()
DownloadManager korzysta z metody Uri.parse() do analizowania lokalizacji żądanego pobierania. Ze względu na wydajność klasa Uri nie przeprowadza weryfikacji niezaufanych danych wejściowych lub przeprowadza ją w niewielkim stopniu.
Wpływ
Korzystanie z DownloadManager może prowadzić do luk w zabezpieczeniach poprzez wykorzystanie uprawnień WRITE do pamięci zewnętrznej. Uprawnienia android.permission.WRITE_EXTERNAL_STORAGE zapewniają szeroki dostęp do pamięci zewnętrznej, dlatego osoba przeprowadzająca atak może po cichu modyfikować pliki i pobrane dane, instalować potencjalnie szkodliwe aplikacje, odmawiać dostępu do usług podstawowych aplikacji lub powodować ich awarie. Złośliwi użytkownicy mogą też manipulować danymi wysyłanymi do funkcji Uri.parse(), aby spowodować pobranie przez użytkownika szkodliwego pliku.
Środki ograniczające ryzyko
Zamiast korzystać z klasy DownloadManager, skonfiguruj pobieranie bezpośrednio w aplikacji za pomocą klienta HTTP (np. Cronet), harmonogramu/menedżera procesów i sposobu zapewnienia ponownych prób w przypadku utraty połączenia z siecią. Dokumentacja biblioteki zawiera link do przykładowej aplikacji oraz instrukcje dotyczące jej implementacji.
Jeśli aplikacja wymaga możliwości zarządzania harmonogramem procesów, pobierania w tle lub ponawiania pobierania po utracie połączenia z siecią, rozważ uwzględnienie WorkManager i ForegroundServices.
Przykładowy kod konfiguracji pobierania za pomocą Croneta pochodzi z ćwiczeń praktycznych dotyczących Croneta:
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();
});
}
Zasoby
- Główna strona dokumentacji DownloadManagera
- Raport dotyczący luk w zabezpieczeniach usługi DownloadManager
- Obejście uprawnień Androida CVE 2018-9468
- Wstrzyknięcie kodu SQL w usłudze Android Download Provider CVE-2018-9493
- Obejście uprawnień dostawcy pobierania na Androidzie CVE2018-9468
- Główna strona dokumentacji Cronet
- Instrukcje korzystania z Cronet w aplikacji
- Przykładowa implementacja Croneta
- Dokumentacja Uri
- Dokumentacja klasy ForegroundService
- Dokumentacja WorkManagera