Kategoria OWASP: MASVS-CODE: Code Quality
Omówienie
Podczas przechowywania lub przesyłania dużych ilości danych obiektów Java często bardziej wydajne jest najpierw zserializowanie tych danych. Dane są następnie poddawane deserializacji przez aplikację, działanie lub dostawcę, który je obsługuje. W normalnych okolicznościach dane są serializowane, a następnie deserializowane bez udziału użytkownika. Jednak relacja zaufania pomiędzy procesem deserializacji a jego docelowym obiektem może zostać wykorzystana przez osobę o złośliwych zamiarach, która może na przykład przechwycić i zmienić serializowane obiekty. Dzięki temu haker może przeprowadzać ataki takie jak DoS, eskalacja uprawnień i zdalne wykonywanie kodu (RCE).
Choć klasa Serializable
jest powszechną metodą zarządzania serializacją, Android ma własną klasę do obsługi serializacji o nazwie Parcel
. Za pomocą klasy Parcel
można serializować dane obiektu w postaci danych strumienia bajtów i pakować je w Parcel
za pomocą interfejsu Parcelable
.
Umożliwia to efektywniejsze transportowanie i przechowywanie Parcel
.
Należy jednak zachować ostrożność podczas korzystania z klasy Parcel
, ponieważ jest ona przeznaczona do wydajnego mechanizmu transportu IPC, ale nie powinna być używana do przechowywania serializowanych obiektów w lokalnym trwałym magazynie, ponieważ może to spowodować problemy z zgodnością danych lub utratę danych. Gdy dane wymagają odczytania, interfejs Parcelable
może być użyty do deserializacji Parcel
i przekształcenia go z powrotem w dane obiektu.
Istnieją 3 główne wektory wykorzystywania deserializacji w Androidzie:
- Wykorzystanie przez dewelopera błędnego założenia, że deserializacja obiektów pochodzących z klasy niestandardowej jest bezpieczna. W rzeczywistości każdy obiekt pochodzący z dowolnej klasy może zostać zastąpiony szkodliwą zawartością, która w najgorszym przypadku może zakłócać działanie modułów ładowania tej samej lub innych klas aplikacji. Ta ingerencja polega na wstrzykiwaniu niebezpiecznych wartości, które zgodnie z celem klasy mogą prowadzić na przykład do wycieku danych lub przejęcia konta.
- Wykorzystywanie metod deserializacji, które z założenia nie są uznawane za niebezpieczne (np. CVE-2023-35669, lokalna luka w eskalacji uprawnień, która umożliwiała wstrzykiwanie dowolnego kodu JavaScript za pomocą wektora deserializacji precyzyjnego linku)
- Wykorzystanie błędów w logice aplikacji (np. CVE-2023-20963, błąd eskalacji uprawnień lokalnych, który umożliwiał aplikacji pobranie i uruchomienie kodu w środowisku uprzywilejowanym przez błąd w logice paczki WorkSource na Androidzie).
Wpływ
Każda aplikacja, która przeprowadza deserializację niezaufanych lub szkodliwych danych seryjnych, może być narażona na zdalne wykonanie kodu lub ataki typu DoS.
Zagrożenie: deserializacja niezaufanych danych wejściowych
Atakujący może wykorzystać brak weryfikacji pakietu w logice aplikacji, aby wstrzyknąć dowolne obiekty, które po deserializacji mogłyby zmusić aplikację do wykonania złośliwego kodu, co może spowodować odmowę usługi (DoS), zwiększenie uprawnień i zdalne wykonanie kodu (RCE).
Tego typu ataki mogą być subtelne. Aplikacja może na przykład zawierać intencję oczekującą tylko 1 parametru, który po sprawdzeniu zostanie zdeserializowany. Jeśli atakujący wyśle drugi, nieoczekiwany złośliwy parametr dodatkowy wraz z oczekiwanym, spowoduje to zdeserializowanie wszystkich wstrzykniętych obiektów danych, ponieważ intent traktuje dodatki jako Bundle
. Szkodliwy użytkownik może w ten sposób wstrzykiwać dane obiektów, co po deserializacji może doprowadzić do RCE, naruszenia bezpieczeństwa lub utraty danych.
Łagodzenie
Zalecamy, aby zakładać, że wszystkie serializowane dane są niezaufane i potencjalnie szkodliwe. Aby zapewnić integralność serializowanych danych, zweryfikuj je, aby upewnić się, że mają prawidłową klasę i format oczekiwany przez aplikację.
Możliwym rozwiązaniem może być wdrożenie w bibliotece wzoru wyprzedzającego.java.io.ObjectInputStream
Modyfikując kod odpowiedzialny za deserializację, możesz zadbać o to, aby w ramach intencji deserializowany był tylko wyraźnie określony zestaw klas.
W Androidzie 13 (poziom API 33) zaktualizowano kilka metod klasy Intent
, które są uważane za bezpieczniejsze alternatywy dla starszych i teraz przestarzałych metod obsługi paczek. Te nowe metody bezpieczniejszego typowania, takie jak getParcelableExtra(java.lang.String, java.lang.Class)
i getParcelableArrayListExtra(java.lang.String, java.lang.Class)
, przeprowadzają kontrole typu danych w celu wykrywania niespójności, które mogą powodować awarie aplikacji i być potencjalnie wykorzystywane do przeprowadzania ataków polegających na eskalacji uprawnień, takich jak CVE-2021-0928.
Poniższy przykład pokazuje, jak można wdrożyć bezpieczną wersję klasy Parcel
:
Załóżmy, że klasa UserParcelable
implementuje interfejs Parcelable
i tworzy instancję danych użytkownika, które są następnie zapisywane w obiekcie Parcel
. Do odczytania zaszyfrowanej paczki można użyć bezpieczniejszej metody readParcelable
:
Kotlin
val parcel = Parcel.obtain()
val userParcelable = parcel.readParcelable(UserParcelable::class.java.classLoader)
Java
Parcel parcel = Parcel.obtain();
UserParcelable userParcelable = parcel.readParcelable(UserParcelable.class, UserParcelable.CREATOR);
Zwróć uwagę na przykładowy kod w Javie powyżej użycia UserParcelable.CREATOR
w metodzie. Ten wymagany parametr informuje metodę readParcelable
, jakiego typu danych oczekiwać. Jest on wydajniejszy niż wycofana wersja metody readParcelable
.
Konkretne zagrożenia
W tej sekcji zebraliśmy zagrożenia, które wymagają niestandardowych strategii łagodzenia lub zostały złagodzone na określonym poziomie pakietu SDK.
Zagrożenie: niechciana deserializacja obiektu
Implementacja interfejsu Serializable
w klasie spowoduje automatyczne zaimplementowanie go we wszystkich podtypach danej klasy. W takim przypadku niektóre obiekty mogą dziedziczyć wspomniany interfejs, co oznacza, że określone obiekty, które nie mają być deserializowane, będą nadal przetwarzane.
Może to niezamierzenie zwiększyć powierzchnię ataku.
Łagodzenie
Jeśli klasa dziedziczy interfejs Serializable
, zgodnie z wytycznymi OWASP metoda readObject
powinna być implementowana w taki sposób, aby uniknąć deserializacji zestawu obiektów w klasie:
Kotlin
@Throws(IOException::class)
private final fun readObject(in: ObjectInputStream) {
throw IOException("Cannot be deserialized")
}
Java
private final void readObject(ObjectInputStream in) throws java.io.IOException {
throw new java.io.IOException("Cannot be deserialized");
}
Materiały
- Parcelables
- Paczka
- Można serializować
- Intencja
- Wady związane z deserializacją w Androidzie: krótkie omówienie
- Android Parcels: The Bad, the Good and the Better (video)
- Android Parcels: The Bad, the Good and the Better (presentation slides)
- CVE-2014-7911: Android <5.0 – eskalacja uprawnień za pomocą ObjectInputStream
- CVE-CVE-2017-0412.
- CVE-2021-0928: niezgodność serializacji lub deserializacji
- Wskazówki OWASP