
IDOR + XSS stocké via une autorisation au niveau objet cassée dans JoomGallery
JoomGallery ≤ 4.3.0 — Un utilisateur au rôle Éditeur détourne n'importe quelle image de la galerie et stocke une charge utile XSS, permettant la prise de contrôle de la session administrateur
UserimageController::save() dans JoomGallery vérifie checkACL('edit', ...) au lieu de checkACL('edit.own', ...). Un utilisateur au rôle Éditeur peut envoyer une requête POST vers task=userimage.save&id=N pour n'importe quelle image, indépendamment de la propriété (IDOR — CWE-639). Étant donné que le rôle Éditeur possède core.edit globalement, le contrôle d'autorisation réussit pour chaque ID d'image du site, y compris les images appartenant aux administrateurs.
Combiné à un appel $this->escape() manquant dans le template frontal de l'image, un Éditeur peut stocker une charge utile XSS dans n'importe quel titre d'image — y compris les images appartenant aux administrateurs — provoquant l'exécution de JavaScript dans le navigateur de chaque visiteur. Cela permet le détournement complet de la session administrateur et la compromission totale du site.
| COMPOSANT | VULNÉRABLE | TESTÉ SUR | CORRIGÉ |
|---|---|---|---|
| JoomGallery (com_joomgallery) | 4.0.0 – 4.3.0 | Joomla 5.4.7 + JoomGallery 4.3.0-stable (PHP 8.2 / Apache) | 4.4.0 |
Type : Autorisation au niveau de l'objet cassée / IDOR (CWE-639) chaînée avec du Cross-Site Scripting stocké (CWE-79) Authentification requise : Compte Éditeur à faibles privilèges
Fichier : components/com_joomgallery/src/Controller/UserimageController.php
L'action save() effectue un contrôle ACL en utilisant la permission edit au lieu de edit.own. La permission edit est accordée globalement à tous les utilisateurs du rôle Éditeur, donc le contrôle réussit pour n'importe quel ID d'image, quel que soit son créateur.
USERIMAGECONTROLLER.PHP — CODE VULNÉRABLE (LIGNE 145)
// Vulnérable
if (!$this->checkACL('edit', 'image', $recordId, $parent_id, true)) { ... }
Parce que core.edit est détenu globalement par le groupe Éditeur, la condition est évaluée à false pour chaque ID d'image, accordant un accès en écriture sans restriction. Lors d'une sauvegarde réussie, le modèle met également à jour created_by avec l'ID utilisateur de l'attaquant, transférant silencieusement la propriété de l'image à l'attaquant.
Fichier : components/com_joomgallery/tmpl/image/default.php
Le template frontal de l'image affiche $this->item->title sans l'encoder en HTML dans le contexte de l'attribut alt. Le filtre STRING de JInput de Joomla ne supprime pas les guillemets doubles, donc une charge utile contenant " sort de l'attribut et injecte des gestionnaires d'événements arbitraires.
DEFAULT.PHP — CODE VULNÉRABLE (LIGNES 64, 78)
// Vulnérable
item->title; ?>" ...>
La charge utile abc" onmouseover="alert(document.domain);" x=" est stockée dans jos_joomgallery.title et injectée brute dans l'attribut HTML à chaque rendu de page. Aucune désinfection n'est effectuée au niveau du stockage ou de l'affichage.
task=userimage.save&id=3 avec la charge utile XSS dans jform[title]. Le contrôle ACL réussit (core.edit, pas edit.own). Le serveur renvoie HTTP 303 — pas 403.created_by est transféré à l'ID utilisateur de l'attaquant.jos_joomgallery.title.alt="abc" onmouseover="alert(document.domain);". Le XSS se déclenche. La session administrateur est capturée → compromission totale du site.L'administrateur crée admin_image via le backend JoomGallery (Joomla 5.4.7). L'image est publiée, approuvée et appartient à Administrateur

GET /index.php/component/users/login — le JSON de réponse contient "csrf.token":"a68c2b3a...". Le jeton est capturé pour la requête POST de connexion suivante.

joomla_user_state=logged_inPOST /index.php/component/users/login avec le jeton CSRF et les identifiants de l'Éditeur. Réponse : HTTP 303 et Set-Cookie: joomla_user_state=logged_in. Le cookie de session est capturé.

GET /index.php?option=com_joomgallery avec le cookie de session. La réponse contient un nouveau "csrf.token":"2d96934b..." à utiliser dans la requête de sauvegarde.

L'Éditeur envoie une requête POST vers option=com_joomgallery&task=userimage.save&id=3 avec jform[title] défini sur :
abc" onmouseover="alert(document.domain);" x="
Le serveur renvoie HTTP 303 (pas 403), confirmant l'IDOR. L'en-tête Location montre la charge utile XSS dans l'URL de redirection, confirmant que le titre a été accepté et sauvegardé.

Le backend JoomGallery montre que l'image ID=3 a désormais pour propriétaire : Editor User. Le champ created_by a été silencieusement mis à jour dans la base de données lors de la sauvegarde non autorisée.

La requête SQL sur jos_joomgallery confirme que la charge utile XSS est stockée — " est stocké comme un guillemet double brut, et non comme ". Aucune désinfection n'a été effectuée au niveau du stockage.

Tout utilisateur visitant /index.php/component/joomgallery/gallery déclenche la charge utile. La boîte de dialogue alert() du navigateur confirme l'exécution de JavaScript dans l'origine de la victime (document.domain).

created_by à l'attaquant, modifiant définitivement la piste d'audit.alt peut exfiltrer le cookie de session de l'administrateur, donnant à l'attaquant un accès complet au backend et le contrôle de toute l'installation Joomla.S:C), franchissant la frontière de confiance entre la session à faibles privilèges de l'attaquant et la session à hauts privilèges de la victime.