PHP object injection
| Po angielsku | PHP object injection |
|---|---|
| Którą formą pisać | po angielsku Polski odpowiednik istnieje, ale w praktyce nikt tak nie pisze. Zostaw angielski, polski trzymaj jako synonim. |
| Inne formy | wstrzyknięcie obiektu PHP · niebezpieczna deserializacja obiektów PHP |
| Kategoria | Podatności i klasy błędów |
PHP object injection to podatność w aplikacjach PHP, w której dane kontrolowane przez atakującego trafiają do funkcji unserialize(). OWASP opisuje ją jako lukę na poziomie aplikacji, która pozwala przeprowadzić różne ataki, między innymi wstrzyknięcie kodu, SQL injection, path traversal i odmowę usługi (DoS) [1]. W katalogu CWE nazwa PHP Object Injection figuruje jako alternatywne określenie słabości CWE-502, czyli deserializacji niezaufanych danych (insecure deserialization) [2].
Jak działa
Funkcja serialize() zamienia wartość PHP, także obiekt, w ciąg znaków, a unserialize() odtwarza z niego wartość. Zapis obiektu zawiera nazwę klasy i wartości pól. Kto kontroluje ten ciąg, ten decyduje, obiekt jakiej klasy powstanie i jakie będzie miał pola. Dokumentacja PHP ostrzega, że odtworzenie danych może skończyć się załadowaniem i wykonaniem kodu przez utworzenie obiektu i automatyczne ładowanie klas [3].
Sam obiekt nic nie robi. Szkodę wyrządzają metody magiczne, które PHP wywołuje automatycznie: __wakeup() zaraz po odtworzeniu obiektu, __destruct() przy jego niszczeniu, __toString() przy zamianie na tekst [4]. OWASP wymienia dwa warunki udanego ataku [1]:
- aplikacja ma klasę z metodą magiczną, taką jak
__wakeupalbo__destruct, którą można wykorzystać do ataku; - wszystkie klasy użyte w ataku są zadeklarowane w chwili wywołania podatnego
unserialize().
Jeśli jedna metoda magiczna wywołuje metodę innego obiektu zapisanego w polu, atakujący może połączyć kilka klas w łańcuch. Taki łańcuch nazywa się POP (property-oriented programming), a jego ogniwa to gadżety [1]. Przebieg ataku pokazuje schemat poniżej.
Skutki
Skutek zależy od tego, jakie klasy są dostępne w aplikacji, a nie od samego błędu. Już samo odtworzenie obiektu narusza integralność: MITRE ostrzega, że atakujący może zmienić obiekty lub dane, które aplikacja uważała za chronione przed zmianą [2]. MITRE zaznacza, że konsekwencje wykonania zależą od kontekstu i typów odtwarzanych obiektów [2].
| Skutek | Przykładowy gadżet | Źródło |
|---|---|---|
| zdalne wykonanie kodu (RCE) | klasa wywołująca funkcję z pola obiektu | [1] |
| usunięcie dowolnego pliku | destruktor czyszczący pamięć podręczną | [4] |
| SQL injection | metoda budująca zapytanie z pól obiektu | [1] |
| atak odmowy usługi (DoS) | pętla zależna od danych | [2] |
W zestawieniu CWE Top 25 za 2025 rok słabość CWE-502 zajęła 15. miejsce [8].
W WordPressie
WordPress sam intensywnie używa serializacji: w tabelach opcji i metadanych zapisuje tablice i obiekty w postaci zserializowanej. Funkcja maybe_unserialize() odtwarza dane tylko wtedy, gdy wyglądają na zserializowane, i wywołuje przy tym zwykłe unserialize() [5]. Nie ogranicza przy tym dopuszczalnych klas [5]. Dopóki dane w bazie pochodzą od samej aplikacji, jest to bezpieczne. Problem powstaje, gdy wtyczka przekazuje do unserialize() albo maybe_unserialize() ciasteczko, parametr żądania czy pole formularza.
Rdzeń WordPressa zawiera wiele klas, które mogą posłużyć jako gadżety. W październiku 2023 roku WPScan opisał łańcuch prowadzący do zdalnego wykonania kodu, zbudowany wyłącznie z klas rdzenia, w tym WP_Theme z metodą __toString, WP_Block_List i klas biblioteki Requests [6]. Łańcuch wymagał osobnej luki, która pozwoli wstrzyknąć zserializowany obiekt, na przykład przez wstrzyknięcie SQL do pól metadanych [6]. Kolejne ogniwa korzystały z interfejsu ArrayAccess i z mechanizmu haków biblioteki Requests, który pozwalał wywołać dowolną funkcję zwrotną [6]. WordPress 6.3.2 zablokował odtwarzanie kilku klas z tego łańcucha albo ograniczył wartości ich pól [6]. Przykład pokazuje, że nawet aktualny rdzeń nie chroni strony, jeśli jedna z wtyczek odtwarza obiekty z niezaufanych danych: gadżety już w kodzie są, brakuje tylko punktu wejścia.
Jak się bronić
Najprostsza zasada brzmi: nie przekazywać do unserialize() danych od użytkownika. Dokumentacja PHP zaznacza, że dotyczy to także przypadku, gdy ustawiono opcję allowed_classes [3]. Jako format wymiany danych z użytkownikiem zaleca JSON, czyli json_decode() i json_encode() [3]. To samo zalecają OWASP [1] i Michał Bentkowski w artykule Sekuraka [4].
// dane od klienta: tylko JSON, bez obiektów PHP
$dane = json_decode( wp_unslash( $_POST['ustawienia'] ?? '' ), true );
// dane z własnego źródła: bez tworzenia obiektów
$lista = unserialize( $zapis, array( 'allowed_classes' => false ) );
Opcja allowed_classes ustawiona na false sprawia, że zamiast obiektu dowolnej klasy powstaje obiekt __PHP_Incomplete_Class [3]. Gdy zserializowane dane muszą przejść przez klienta, dokumentacja PHP zaleca sprawdzenie ich podpisem hash_hmac() [3]. MITRE również wymienia podpisywanie danych kodem HMAC i listy dozwolonych klas, zaznaczając, że sama lista nie wystarcza [2]. Wśród środków dodatkowych MITRE podaje zapory aplikacyjne [2], czyli WAF.
Po stronie właściciela strony najważniejsze są szybka łatka (patch) wtyczki i aktualizacja rdzenia WordPressa, bo poprawki rdzenia utrudniają budowanie łańcuchów z jego klas.
Historia i przykłady
W czerwcu 2014 roku Michał Bentkowski opisał tę klasę podatności na przykładzie Joomli i luki CVE-2013-1453 [4]. Łańcuch zaczynał się w destruktorze klasy wtyczki debugowania i kończył w metodzie czyszczącej pamięć podręczną, co pozwalało usuwać dowolne katalogi [4]. Punktem wejścia była wtyczka, która przyjmowała w parametrze GET zserializowany obiekt zakodowany w Base64 i przekazywała go do unserialize() bez sprawdzenia [4].
6 grudnia 2023 roku WordPress wydał wersję 6.4.2 z poprawką luki opisanej jako zdalne wykonanie kodu, której nie da się wykorzystać bezpośrednio w rdzeniu [7]. Zespół bezpieczeństwa ocenił, że w połączeniu z niektórymi wtyczkami, zwłaszcza w instalacjach Multisite, może mieć wysoką wagę [7].
Przypisy
8- [1] PHP Object Injection · OWASP
- [2] CWE-502: Deserialization of Untrusted Data · MITRE
- [3] unserialize — PHP Manual · The PHP Group
- [4] PHP Object Injection – mało znana, krytyczna klasa podatności · Sekurak
- [5] maybe_unserialize() — Code Reference · WordPress.org
- [6] Finding A RCE Gadget Chain In WordPress Core · WPScan
- [7] WordPress 6.4.2 Maintenance & Security Release · WordPress.org
- [8] 2025 CWE Top 25 Most Dangerous Software Weaknesses · MITRE