Aller au contenu

Upload de fichiers

Les vulnérabilités d'upload de fichiers apparaissent lorsqu'un serveur web permet de déposer des fichiers sans valider suffisamment leur nom, type, contenu ou taille. Même une simple fonction d'upload d'image peut alors servir à déposer des fichiers dangereux — y compris des scripts côté serveur menant à l'exécution de code à distance.

Selon le cas, le simple dépôt du fichier suffit à causer des dégâts ; d'autres attaques requièrent une requête de suivi pour déclencher son exécution. L'impact varie selon la défaillance : un type de fichier mal validé sur un serveur configuré pour exécuter certaines extensions (.php, .jsp) permet de déposer un web shell ; un nom de fichier mal validé permet d'écraser des fichiers critiques ; une taille non contrôlée ouvre la voie à un déni de service par saturation du disque.

Comment un serveur traite les fichiers

Comprendre ce traitement éclaire les exploits. À la réception d'une requête pour un fichier, le serveur analyse son extension pour déterminer son type MIME, à partir d'une liste de correspondances préconfigurée. La suite dépend du type et de la configuration : un fichier non exécutable (image, HTML statique) voit son contenu renvoyé tel quel ; un fichier exécutable sur un serveur configuré pour ce type est exécuté, et son résultat renvoyé ; un fichier exécutable sur un serveur non configuré pour ce type renvoie une erreur — ou, parfois, son contenu en clair, ce qui peut exposer du code source. L'en-tête Content-Type de la réponse renseigne souvent sur le type que le serveur a cru traiter.

Web shell sur upload non restreint

Dans le pire des cas, on dépose un script exécuté par le serveur. Un web shell minimal en PHP permet de lire un fichier arbitraire :

<?php echo file_get_contents('/chemin/vers/fichier'); ?>

Une version plus polyvalente exécute n'importe quelle commande passée en paramètre :

<?php echo system($_GET['command']); ?>

déclenchée ensuite par une simple requête :

GET /uploads/shell.php?command=id HTTP/1.1

Contourner les défauts de validation

Les protections existent presque toujours, mais sont rarement infaillibles.

Validation du type MIME via l'en-tête. Les uploads de fichiers utilisent le type multipart/form-data, où chaque partie porte son propre en-tête Content-Type. Si le serveur fait confiance à cet en-tête sans vérifier le contenu réel, il suffit de déclarer un type autorisé (image/jpeg) tout en envoyant un script.

Empêcher l'exécution dans les dossiers d'upload. Une deuxième ligne de défense empêche l'exécution des scripts déposés. Mais cette configuration varie d'un dossier à l'autre : si l'on parvient à déposer un script dans un répertoire non prévu pour les uploads, il peut redevenir exécutable. Le champ filename d'une requête multipart/form-data déterminant souvent l'emplacement de stockage, une traversée de répertoires y est parfois exploitable :

filename="..%2fshell.php"

Liste noire d'extensions. Bloquer .php est fragile : il existe trop d'extensions exécutables. Les listes noires se contournent par des extensions alternatives selon la configuration (.php5, .shtml, .phtml).

Fichiers de configuration du serveur. Beaucoup de serveurs chargent des fichiers de configuration locaux à un dossier. Si l'upload de tels fichiers n'est pas bloqué, on peut redéfinir le traitement des extensions. Sur Apache, un .htaccess déposé dans le dossier d'upload associe une extension arbitraire au moteur PHP :

AddType application/x-httpd-php .l33t

Sur IIS, un web.config joue un rôle équivalent.

Obfuscation de l'extension. Même une liste noire exhaustive cède aux techniques classiques : casse mélangée (.pHp) si la validation est sensible à la casse mais pas le mapping MIME ; double extension (exploit.php.jpg) ; caractères de fin (exploit.php., espaces) ; encodage d'URL simple ou double des points et barres obliques ; octet nul ou point-virgule avant l'extension (exploit.php%00.jpg, exploit.php;.jpg) exploitant une divergence entre validation haut niveau et traitement bas niveau ; caractères Unicode multi-octets convertis en point après normalisation. Contre un retrait non récursif d'extension interdite, on construit le nom de sorte que la suppression laisse une extension valide :

exploit.p.phphp

Validation du contenu. Les serveurs plus sûrs vérifient que le contenu correspond au type : dimensions pour une image, ou signature en en-tête (un JPEG commence par les octets FF D8 FF). Cette méthode est plus robuste mais pas infaillible : un fichier polyglotte mêle une image valide et du code. Avec ExifTool, on injecte un payload PHP dans les métadonnées d'une image, qui conserve une signature d'image valide :

exiftool -Comment="<?php echo system($_GET['command']); ?>" image.jpg -o polyglot.php

Race condition à l'upload

Les frameworks modernes déposent d'abord le fichier dans un dossier temporaire, le valident, puis ne le déplacent qu'une fois jugé sûr. Mais une implémentation maison peut introduire une race condition : certains sites déposent le fichier à sa destination finale puis le suppriment s'il échoue à la validation (typique des vérifications antivirus). Pendant le bref instant où le fichier existe — quelques millisecondes — un attaquant peut l'exécuter. Ces failles sont subtiles et difficiles à détecter en boîte noire.

Le même type de course survient sur les uploads par URL, où le serveur télécharge le fichier puis le valide manuellement. Si le nom temporaire est généré par une fonction pseudo-aléatoire prévisible (comme uniqid() en PHP), il peut être brute-forcé. Pour élargir la fenêtre d'attaque, on augmente le temps de traitement en déposant un fichier volumineux dont le payload figure au début, suivi d'un large remplissage.

Exploitation sans RCE

L'exécution de code n'est pas la seule issue. Côté client, déposer un fichier HTML ou SVG permet d'y placer une charge XSS stockée, qui s'exécutera quand un autre utilisateur consultera le fichier — à condition qu'il soit servi depuis la même origine (politique de même origine). On peut aussi viser une vulnérabilité de parsing : les formats fondés sur XML (documents Office) ouvrent la voie à une injection XXE.

Upload via la méthode PUT

Certains serveurs sont configurés pour accepter les requêtes PUT. En l'absence de défense, cela offre une voie d'upload alternative, même sans fonction d'upload dans l'interface :

PUT /images/exploit.php HTTP/1.1
Host: site-vulnerable.example
Content-Type: application/x-httpd-php

<?php echo system($_GET['command']); ?>

Une requête OPTIONS sur différents endpoints permet de repérer ceux qui annoncent la prise en charge de PUT.

Aide-mémoire

Défense rencontrée Contournement
Aucune Web shell direct (.php)
Type MIME via en-tête Déclarer image/jpeg, envoyer un script
Pas d'exécution dans le dossier Traversée via filename vers un autre dossier
Liste noire d'extensions Extensions alternatives (.php5, .phtml)
Liste noire stricte .htaccess / web.config redéfinissant le mapping
Validation de l'extension Obfuscation (casse, double extension, octet nul, Unicode)
Validation du contenu Fichier polyglotte (ExifTool)
Implémentation maison Race condition (dépôt avant validation)
Pas de fonction d'upload Requête PUT

Repérer la couche de défense exacte — type, contenu, exécution, nom — oriente directement vers la technique de contournement adaptée ; c'est le premier diagnostic à poser face à une fonction d'upload.