
Joomla 6.1.3 - Alta - Remota - ACL - POST /api/v1/content/articles ignora a ACL por categoria
abraxaslabs.tech · github.com/abraxas · @abraxas_null · [email protected] · joomla-articles-catid
Write-up: https://abraxaslabs.tech/research/joomla-articles-catid
Classe: Privilégio residual Alcance: Remoto
Joomla CMS 6.1.3 (0f2d4de) - Open Source Matters
O ArticleController::allowAdd do administrador verifica com_content.category.{catid}. api/components/com_content/src/Controller/ArticlesController.php não possui allowAdd. Em seguida, ApiController::add() chama allowAdd() sem dados, então o catid do JSON nunca entra na verificação. O allowAdd base é verdadeiro se o usuário puder criar em qualquer categoria.
Um usuário de API da classe Manager, com core.login.api e permissão de criação em uma categoria, pode enviar um artigo via POST para uma categoria na qual está negado na interface.
| ID | ainda sem CVE |
| Classe | Privilégio residual / bypass de autorização |
| Alcance | Remoto (token de API com core.login.api) |
| CWE | CWE-863 |
| CVSS | Médio: 6.5 CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:H/A:N |
| Produto | Joomla CMS |
| Afetado | 6.1.3 (0f2d4de4729eb951e0878fec98c7990da07915b2), POST /api/index.php/v1/content/articles |
| Autenticação | Token de API para Manager (grupo 6) com core.login.api, core.manage de com_content e core.create em uma categoria apenas. Sem Super User. O padrão de core.login.api é apenas Super User. |
| Licença | GNU Affero GPL v3.0 |
| Lab | apenas 127.0.0.1 |
Usar um token de API de não-Super-User que pode criar em uma categoria para publicar em categorias das quais está banido. Ele não se torna Super User a partir desse resíduo. Trata-se de publicação não autorizada, não de tomada de controle do site nem de leitura de artigos ocultos.
| Privilégio | Método / caminho | Tipo | Comprovado em laboratório ou inferido |
|---|---|---|---|
| Criar um artigo numa categoria à qual está negado | POST /api/index.php/v1/content/articles JSON catid de B | integridade | Comprovado em laboratório. HTTP 200, #__content id=1, catid=9 (Press), título poc-art-JOOMLA-ARTICLES-CATID-WITNESS |
| Plantar cópia publicada em Avisos oficiais / Press enquanto apenas Rascunhos são permitidos | Mesmo POST, state=1 | integridade | Comprovado em laboratório criar numa Press negada. Inferido para qualquer outro recurso de categoria com core.create negado |
| Editar um artigo existente via API | PATCH /api/index.php/v1/content/articles/{id} | n/a | Inferido fechado para este Manager. O allowEdit base requer core.edit do componente; as regras de laboratório de com_content dão core.edit a Author (4), não a Manager (6). O laboratório não fez PATCH |
| Eliminar um artigo via API | DELETE /api/index.php/v1/content/articles/{id} | n/a | Inferido fechado. O allowDelete base requer core.delete do componente. O laboratório não fez DELETE |
| Guardar um artigo na categoria permitida A a partir do administrator | POST ... task=article.save jform[catid]=A | pretendido | Comprovado em laboratório aberto conforme pretendido. HTTP 200, não negado |
| Guardar um artigo na categoria negada B a partir do administrator | Mesma tarefa, jform[catid]=B | n/a | Comprovado em laboratório fechado. HTTP 200 com "not permitted" / create-not-permitted (UI allowAdd em com_content.category.B) |
Atingir a API com um Manager padrão (sem core.login.api) | Bearer token / X-Joomla-Token | n/a | Inferido fechado. O core.login.api padrão é apenas Super User (grupo 8). Este laboratório concede-o primeiro ao grupo 6 |
| Mesma lacuna em contacts / banners / newsfeeds | POST /api/index.php/v1/contacts (e semelhantes) com catid estrangeiro | integridade | Inferido. Esses controladores da API também não têm allowAdd. O laboratório não os exercitou |
| RCE de Super User / instalador | n/a | n/a | Fechado. O Manager permanece Manager |
Fixe-me na 6.1.3 após a vaga JSST de 2026-07/08. A API de Groups já requer core.admin. O webservice de Media já requer core.edit na substituição. A API de Articles ainda passa pelo ApiController e trata "criar em qualquer categoria" como suficiente para todos os catid.
public function add()
{
if (!$this->allowAdd()) {
throw new NotAllowed('JLIB_APPLICATION_ERROR_CREATE_RECORD_NOT_PERMITTED', 403);
}
$recordId = $this->save();
$this->displayItem($recordId);
}
Verificação base:
protected function allowAdd($data = [])
{
$user = $this->app->getIdentity();
if (!$user->authorise('core.manage', $this->option)) {
return false;
}
return $user->authorise('core.create', $this->option)
|| \count($user->getAuthorisedCategories($this->option, 'core.create'));
}
A UI de Administrador já depende de catid:
protected function allowAdd($data = [])
{
$categoryId = ArrayHelper::getValue(
$data,
'catid',
$this->input->getInt('filter_category_id'),
'int'
);
if ($categoryId) {
return $this->app->getIdentity()->authorise(
'core.create',
'com_content.category.' . $categoryId
);
}
return parent::allowAdd();
}
Caminhos errados já registrados: ler HTTP 200 da UI como permitido (a negação de B é o corpo "not permitted"); tratar o componente core.create como a falha (o Manager do lab não tem nenhum; uma permissão de categoria já basta para a contagem base de getAuthorisedCategories); um Manager padrão sem core.login.api. Teatro: um reverse shell. O oráculo é API 200 no Press mais UI permitindo em Drafts mais UI negando em Press.
Sobras do mesmo pin: levels API, menu items API, media override.
python3 joomla-articles-catid-Abraxas-Labs.py
ou
cd lab
./run.sh
Aponte apenas para http://127.0.0.1:19550. Imagem joomla:6.1.3-php8.3-apache mais mariadb:11.4.
SUCCESS joomla-articles-catid api-http=200 article-id=1 catid=9 cat-a=8 cat-b=9 ui-a=200 ui-b=200 JOOMLA-ARTICLES-CATID-WITNESS
article-id é o próximo autoincremento de #__content (1 em uma instalação limpa do 6.1.3). ui-a / ui-b retornam HTTP 200; a negação de B é o corpo "not permitted" da UI, não um status 403.