
IDOR + XSS Armazenado via Autorização de Nível de Objeto Quebrada no JoomGallery
JoomGallery ≤ 4.3.0 — Usuário com Papel de Editor Sequestra Qualquer Imagem da Galeria e Armazena Payload XSS, Permitindo a Tomada de Sessão do Admin
UserimageController::save() no JoomGallery verifica checkACL('edit', ...) em vez de checkACL('edit.own', ...). Um usuário com papel de Editor pode fazer POST para task=userimage.save&id=N de qualquer imagem, independentemente da propriedade (IDOR — CWE-639). Como o papel de Editor carrega core.edit globalmente, a verificação de autorização passa para cada ID de imagem no site, incluindo imagens de propriedade dos administradores.
Combinado com a ausência de uma chamada $this->escape() no template de imagem do frontend, um Editor pode armazenar um payload XSS em qualquer título de imagem — inclusive imagens de propriedade do admin — causando a execução de JavaScript no navegador de todos os visitantes. Isso possibilita o sequestro completo da sessão do admin e o comprometimento de todo o site.
| COMPONENT | VULNERÁVEL | TESTADO EM | CORRIGIDO |
|---|---|---|---|
| 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 |
Tipo: Autorização de Nível de Objeto Quebrada / IDOR (CWE-639) encadeada com Cross-Site Scripting Armazenado (CWE-79) Autenticação necessária: Conta de Editor com privilégios baixos
Arquivo: components/com_joomgallery/src/Controller/UserimageController.php
A ação save() realiza uma verificação de ACL usando a permissão edit em vez de edit.own. A permissão edit é concedida globalmente a todos os usuários com papel de Editor, portanto a verificação é bem-sucedida para qualquer ID de imagem, independentemente de quem a criou.
USERIMAGECONTROLLER.PHP — CÓDIGO VULNERÁVEL (LINHA 145)
// Vulnerable
if (!$this->checkACL('edit', 'image', $recordId, $parent_id, true)) { ... }
Como core.edit é mantido globalmente pelo grupo Editor, a condição é avaliada como false para cada ID de imagem, concedendo acesso de escrita irrestrito. Em um salvamento bem-sucedido, o modelo também atualiza created_by para o ID de usuário do atacante, transferindo silenciosamente a propriedade da imagem para o atacante.
Arquivo: components/com_joomgallery/tmpl/image/default.php
O template de imagem do frontend exibe $this->item->title sem codificação HTML no contexto do atributo alt. O filtro STRING do JInput do Joomla não remove caracteres de aspas duplas, portanto um payload contendo " escapa do atributo e injeta manipuladores de eventos arbitrários.
DEFAULT.PHP — CÓDIGO VULNERÁVEL (LINHAS 64, 78)
// Vulnerable
item->title; ?>" ...>
O payload abc" onmouseover="alert(document.domain);" x=" é armazenado em jos_joomgallery.title e injetado cru no atributo HTML a cada renderização de página. Nenhuma sanitização ocorre na camada de armazenamento ou exibição.
task=userimage.save&id=3 com o payload XSS em jform[title]. A verificação de ACL passa (core.edit, não edit.own). O servidor retorna HTTP 303 — não 403.created_by é transferido para o ID de usuário do atacante.jos_joomgallery.title.alt="abc" onmouseover="alert(document.domain);". O XSS é acionado. A sessão do admin é capturada → comprometimento total do site.O admin cria admin_image pelo backend do JoomGallery (Joomla 5.4.7). A imagem é Publicada, Aprovada e de propriedade do Administrador

GET /index.php/component/users/login — a resposta JSON contém "csrf.token":"a68c2b3a...". Token capturado para o POST de login subsequente.

joomla_user_state=logged_inPOST /index.php/component/users/login com o token CSRF e as credenciais do Editor. Resposta: HTTP 303 e Set-Cookie: joomla_user_state=logged_in. Cookie de sessão capturado.

GET /index.php?option=com_joomgallery com o cookie de sessão. A resposta contém um novo "csrf.token":"2d96934b..." para uso na requisição de salvamento.

O Editor faz POST para option=com_joomgallery&task=userimage.save&id=3 com jform[title] definido como:
abc" onmouseover="alert(document.domain);" x="
O servidor retorna HTTP 303 (não 403), confirmando o IDOR. O cabeçalho Location mostra o payload XSS na URL de redirecionamento, confirmando que o título foi aceito e salvo.

O backend do JoomGallery mostra que a imagem ID=3 agora tem Proprietário: Editor User. O campo created_by foi silenciosamente atualizado no banco de dados durante o salvamento não autorizado.

A consulta SQL em jos_joomgallery confirma que o payload XSS está armazenado — " é armazenado como uma aspa dupla crua, não como ". Nenhuma sanitização ocorreu na camada de armazenamento.

Qualquer usuário que visite /index.php/component/joomgallery/gallery aciona o payload. O diálogo alert() do navegador confirma a execução de JavaScript na origem da vítima (document.domain).

created_by ao atacante, alterando permanentemente a trilha de auditoria.alt pode exfiltrar o cookie de sessão do administrador, concedendo ao atacante acesso total ao backend e controle sobre toda a instalação Joomla.S:C), cruzando o limite de confiança entre a sessão de baixo privilégio do atacante e a sessão de alto privilégio da vítima.