Dzisiaj o funkcji sprawdzającej czy plik o podanej nazwie istnieje na dysku.
Czy ta prosta funkcjonalność może wyrządzić nam jakieś szkody?
Popatrzmy na przykładową aplikację:
<?php
class aplikacja {
function __construct($name) {
$this->name = './pliki/'.$name;
if (strpos($this->name, "..") !== false) {
die('HACKER');
}
if (file_exists($name)) {
echo 'USUNIETO';
} else {
echo 'PLIK NIE ISTNIEJE';
}
}
function __destruct() {
echo 'EXPLOIT'.$this->name;
}
}
new aplikacja($_GET['p']);
Sprawdzamy w niej, czy podany przez użytkownika plik znajduje się na dysku w katalogu pliki.
Korzystamy przy tym z pomocy funkcji file_exists.
Oczywiście jesteśmy świadomi ataku path traversal, w którym użytkownik mógłby użyć ciągu znaków z kropką i slashem w celu sprawdzenia istnienia plików poza katalogiem pliki.
Dlatego też sprawdzamy, czy w podanym przez użytkownika ciągu nie istnieją dwie kropki.
Jeżeli plik istnieje - wyświetlamy odpowiedni komunikat.
Korzystamy też z magicznej funkcji __destruct, która usuwa plik o który prosił użytkownik.
W naszym wypadku symulujemy to usunięcie funkcją echo - tak aby zobaczyć co tak naprawdę dzieje się w tym przykładzie.
Jest to więc prosta funkcjonalność usuwania plików z serwera - na przykład zdjęć użytkownika z danego katalogu.
Jeszcze do niedawna można by było założyć, że używany tutaj kod jest bezpieczny.

Bronimy się przecież przed atakiem path traversal i usuwamy pliki jedynie z jednego katalogu.
Branża bezpieczeństwa to jednak dynamiczny rynek - gdzie wszystko jest możliwe.
Jakiś czas temu przedstawiono nowy atak na aplikacje stworzone w języku PHP.
Do jego przeprowadzenia korzysta się z plików phar. Co to za pliki?

Jest to format podobny do javowych plików jar.
Zatem przy pomocy tego formatu możemy spakować całą naszą wielo-plikową aplikację do jednego, przenośnego pliku będącego swego rodzaju archiwum.
Dlaczego to takie istotne z punktu widzenia bezpieczeństwa?
Niektóre funkcje związane z odczytywaniem plików z dysku oprócz standardowych parametrów obsługują również tak zwane wrappery1.

To właśnie dzięki nim funkcje te mogą obsługiwać dodatkowe formaty.
Takim wrapperem jest protokół http:// czy też ftp://.
Jest nim także phar.
Co dalej? Badacze odkryli, że jeżeli skorzystamy z funkcji obsługującej wrappery - czyli na przykład file_exists i jako parametr przekażemy do niej wrapper phar - to funkcja ta oprócz sprawdzenia, czy plik rzeczywiście istnieje - otworzy także jego zawartość.
Co rozumiem przez słowo "otworzy"?

Archiwum phar oprócz treści plików przechowuje także metadane na temat całej paczki.
A jak możemy wyczytać w dokumentacji2 - są one przechowywane w zserializowanej formie.
W PHP oznacza to, że do ich zapisania używa się funkcji serialize, a do ich odczytania funkcji unserialize.
Funkcja file_exists wykonuje zatem funkcję unserialize na metadanych paczki przekazanej do niej jako parametr.
Jest to więc klasyczny atak object injection - kiedy to dane od atakującego są przekazywane do funkcji unserialize.
Wystarczy zatem stworzyć przykładowy plik phar i w jego treści zawrzeć obiekt, który chcemy przekazać do atakowanej przez nas aplikacji.

Zobaczmy na nasz przykład jeszcze raz.
Naszym celem jest usunięcie pliku poza katalogiem "pliki".
Tworzymy więc paczkę phar a w niej przykładowy obiekt klasy aplikacja.
Tam ustawiamy parametr name na interesującą nas wartość.

Teraz wystarczy już tylko wysłać ten plik na serwer, a następnie wywołać funkcję file_exists używając wrappera phar.
Jak widzisz - został tutaj usunięty pliki spoza katalogu pliki.
Stało się tak, ponieważ treść metadanych została odczytana przez funkcję file_exists.
Nasz test przeciwko atakom nie zadziałał - parametr od użytkownika bowiem nie zawierał podwójnej kropki.

Jaki morał płynie z tego ataku?
W przeszłości wystarczyło sprawdzać, czy nie posiadamy funkcji unserialize w kodzie źródłowym naszej aplikacji.
Teraz należy dodatkowo sprawdzać, czy parametr przekazywany przez użytkownika nie zawiera słowa kluczowego phar.
Icon made by Freepik, Smashicons, Maxim Basinski Premium from www.flaticon.com