11-01-2019 / Od 0 do pentestera

PHAR Injection

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.

Czy 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?

Plik PHAR

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.

Wrappery PHP

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"?

Metadane

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.

PHAR Injection

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ść.

Spreparowane archiwum PHAR

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.

Wykorzystanie podatności w praktyce

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