Zero-Day
10 min czytania
17 marca 2026

Przepis na katastrofę. Nieuwierzytelnione PHP Object Injection w pluginie Cooked Pro (CVE-2022-3900)

Przepis na katastrofę. Nieuwierzytelnione PHP Object Injection w pluginie Cooked Pro (CVE-2022-3900)

Zastanawialiście się kiedyś, co może pójść nie tak, gdy połączymy wtyczkę do kulinarnych przepisów z jedną z najniebezpieczniejszych i najbardziej przestarzałych funkcji w PHP? Otóż można ugotować niezły bałagan.

Podczas moich badań nad bezpieczeństwem popularnych rozszerzeń do WordPressa natrafiłem na klasyczny błąd typu PHP Object Injection (Insecure Deserialization) w pluginie Cooked Pro. Moje zgłoszenie zaowocowało przypisaniem oficjalnego numeru CVE-2022-3900 (zostało m.in. skatalogowane przez WPScan oraz bazę Wiz.io). Luka otrzymała na platformie NVD bezwzględny scoring 9.8 (Critical), choć niektóre bazy oceniają ją na mocne 7.4 (High).

Zobaczmy, dlaczego ślepe ufanie danym od użytkownika i przepuszczanie ich przez funkcję unserialize() to idealny przepis na oddanie serwera w ręce hakera. I to bez konieczności logowania!

TL;DR - Najważniejsze wnioski

  • Co poszło nie tak? (Endpointy i założenia)
  • Proof of Concept (Weryfikacja Koncepcji)
  • Jak ustrzec się przed takimi wpadkami? (Remediacja)

Co poszło nie tak? (Endpointy i założenia)

Wtyczka Cooked Pro (podatna w wersjach starszych niż 1.7.5.7) oferuje bardzo wygodną funkcjonalność, pozwala na doładowywanie kolejnych przepisów na stronie w sposób asynchroniczny (często realizowane przyciskiem "Load More").

Żądanie z frontendu leci prosto do standardowego w ekosystemie WordPressa pliku obsługującego AJAX: /wp-admin/admin-ajax.php, gdzie docelowo wywoływana jest akcja:

action=cooked_loadmore

Wśród przesyłanych parametrów znalazł się jeden bardzo ciekawy element: recipe_args. Deweloperzy wtyczki wpadli na dość niefortunny pomysł, aby argumenty dla przepisu przesyłać ze strony klienta w formie zserializowanego (zapakowanego) obiektu PHP.

Backend pobierał parametr recipe_args prosto z zapytania i od razu wrzucał go do natywnej funkcji unserialize(). Brak autoryzacji? Brak sprawdzania i czyszczenia (sanitization) danych wejściowych? Mamy to. Błąd skatalogowany jako CWE-502 (Deserialization of Untrusted Data) stał otworem.

Dlaczego unserialize() jest niebezpieczne?

Funkcja unserialize() w PHP odtwarza pełne obiekty PHP z ciągu znaków, włącznie z ich klasą i właściwościami. Jeśli atakujący kontroluje dane wejściowe, może stworzyć dowolny obiekt z klasy dostępnej w pamięci aplikacji i wywołać jego metody magiczne (__wakeup, __destruct), co prowadzi do wykonania dowolnego kodu.

CWE-502
9.8Critical
Vector: CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H
Exploitability Metrics
Attack Vector (AV)
Network (N)Adjacent (A)Local (L)Physical (P)
Attack Complexity (AC)
Low (L)High (H)
Privileges Required (PR)
None (N)Low (L)High (H)
User Interaction (UI)
None (N)Required (R)
Scope (S)
Unchanged (U)Changed (C)
Impact Metrics
Confidentiality Impact (C)
None (N)Low (L)High (H)
Integrity Impact (I)
None (N)Low (L)High (H)
Availability Impact (A)
None (N)Low (L)High (H)

Proof of Concept (Weryfikacja Koncepcji)

Aby wykorzystać ten błąd, nie potrzebowaliśmy żadnego konta w systemie WordPress. Atakujący musiał po prostu wysłać do serwera odpowiednio spreparowane żądanie POST:

POST /wp-admin/admin-ajax.php HTTP/1.1 Content-Type: application/x-www-form-urlencoded; charset=UTF-8 Connection: close action=cooked_loadmore &atts%5Bcategory%5D=false &atts%5Border%5D=false &recipe_args=<SERIALIZED_PHP_OBJECT> &page=1 &is_own_profile=

Magia dzieje się w miejscu, które nazwałem <SERIALIZED_PHP_OBJECT>. Kiedy PHP deserializuje obiekty, automatycznie próbuje wywołać w nich tzw. "magiczne metody" (np. __wakeup() czy __destruct()).

Jeśli atakujący podstawi pod recipe_args złośliwy ładunek (tzw. POP chain. Property Oriented Programming), zawierający klasy dostępne w środowisku WordPressa (lub z innych aktywnych wtyczek/motywów), może zmusić serwer do wykonania groźnych operacji:

  • Usunięcie dowolnego pliku na serwerze (np. wp-config.php)
  • Odczytanie bazy danych i kradzież danych użytkowników
  • Zresetowanie hasła administratora
  • Docelowo: pełne Remote Code Execution (RCE)
Podatny kod
Klient (Frontend)recipe_args=O:8:"stdClass":...
POST admin-ajax.php
Backend PHPunserialize($recipe_args)
__wakeup() / __destruct()
RezultatRemote Code Execution (RCE)
Brak autoryzacji + brak sanityzacji = RCE
POP Chain Gadgets

Zaleznie od dostepnych klas w srodowisku WordPress, atakujacy moze:

Usunac dowolny pliknp. wp-config.php
Odczytac baze danychDane uzytkownikow, hasla
Zresetowac haslo adminaPelne przejecie panelu
Remote Code ExecutionPelna kontrola nad serwerem
Bez logowania: kazdy moze wyslac payload

Jak ustrzec się przed takimi wpadkami? (Remediacja)

Błędy typu Insecure Deserialization od dawna plasują się wysoko w zestawieniach OWASP Top 10, a w starych aplikacjach i wtyczkach PHP są wręcz plagą. Przepis na bezpieczny kod składa się zaledwie z dwóch składników:

  • Nigdy nie ufaj unserialize(): Złota i nienaruszalna zasada programowania w PHP. Funkcja unserialize() nigdy nie powinna przyjmować danych pochodzących od użytkownika, z ciasteczek lub z parametrów GET/POST.
  • Przejdź na JSON: Jeśli musisz przesyłać złożone struktury (takie jak wielowymiarowe tablice czy argumenty, jak we wspomnianym recipe_args) pomiędzy klientem a serwerem, zawsze używaj formatu JSON. Zastosowanie natywnych i bezpiecznych funkcji json_encode() oraz json_decode() całkowicie eliminuje to ryzyko.
Aplikacje oparte o WordPressa to potężny ekosystem, a błędy w jednym niszowym module potrafią położyć całą infrastrukturę. Zawsze dbajcie o aktualizacje wtyczek, a jeśli tworzycie je sami, pamiętajcie: niech JSON będzie z Wami!
Marcin Motwicki, CEO PWNONE
Niebezpieczne
unserialize()

Odtwarza pelne obiekty PHP z ciagu znakow, wlacznie z klasami i metodami magicznymi.

// Podatny kod Cooked Pro
$args = $_POST['recipe_args'];
$recipe = unserialize($args);
// Atakujacy wstrzykuje:
O:8:"stdClass":1:{s:4:"exec";s:6:"whoami";}
Obiekty + magiczne metody = RCE
Bezpieczne
json_decode()

Przenosi jedynie wartosci i strukture danych. Ignoruje obiekty i klasy wykonywalne PHP.

// Poprawiony kod
$args = $_POST['recipe_args'];
$recipe = json_decode($args, true);
// JSON przenosi tylko dane:
{"category":"false","order":"false"}
Tylko dane, bez obiektow → Bezpiecznie!

Podsumowanie

CVE-2022-3900 to klasyczny przykład nieuwierzytelnionego PHP Object Injection przez niebezpieczną funkcję unserialize(). Wtyczka Cooked Pro dla WordPressa pozwalała każdemu użytkownikowi internetu na wstrzyknięcie złośliwych obiektów PHP poprzez parametr recipe_args w zapytaniu AJAX. W najgorszym scenariuszu atakujący mógł uzyskać pełny Remote Code Execution na serwerze. Rozwiązanie jest proste, zamiana unserialize() na json_decode() i walidacja danych wejściowych.

Bibliografia

1
CVE-2022-3900, NIST National Vulnerability Database

Oficjalny wpis w bazie NIST NVD opisujący podatność PHP Object Injection w pluginie Cooked Pro z oceną CVSS 9.8 (Critical).

2
CWE-502: Deserialization of Untrusted Data

Oficjalny wpis MITRE opisujący klasę podatności polegającą na deserializacji niezaufanych danych, dokładnie ten typ błędu, który został wykorzystany w CVE-2022-3900.

3
WPScan. Cooked Pro < 1.7.5.7, PHP Object Injection

Wpis WPScan dokumentujący podatność PHP Object Injection w pluginie Cooked Pro, włącznie ze szczegółami technicznymi i wersjami dotkniętymi błędem.

4
OWASP, Insecure Deserialization

Przewodnik OWASP dotyczący testowania i zapobiegania atakom typu Insecure Deserialization, jednej z kategorii OWASP Top 10.

5
PHP Manual, unserialize()

Oficjalna dokumentacja PHP dla funkcji unserialize() z ostrzeżeniem bezpieczeństwa: "Do not pass untrusted user input to unserialize()".

Twoje wtyczki WordPress mogą kryć tykającą bombę.

PHP Object Injection, SQL Injection, XSS, niebezpieczne błędy czają się nawet w niszowych pluginach. Audyt PWNONE przeskanuje każdą warstwę Twojej aplikacji.

Zamów audyt bezpieczeństwa WordPress