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 wrappery
1.

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