
Joomla 6.1.3 - Alta - Remota - ACL - POST /api/v1/content/articles ignora las ACL por categoría
abraxaslabs.tech · github.com/abraxas · @abraxas_null · [email protected] · joomla-articles-catid
Análisis: https://abraxaslabs.tech/research/joomla-articles-catid
Clase: Privilegio residual Alcance: Remoto
Joomla CMS 6.1.3 (0f2d4de) - Open Source Matters
La comprobación ArticleController::allowAdd del administrador verifica com_content.category.{catid}. api/components/com_content/src/Controller/ArticlesController.php no tiene allowAdd. Después, ApiController::add() llama a allowAdd() sin datos, por lo que el catid del JSON nunca entra en la comprobación. El allowAdd base es verdadero si el usuario puede crear en cualquier categoría.
Un usuario de API de clase Manager, al que se le ha concedido core.login.api y permiso de creación en una categoría, puede enviar un artículo mediante POST a una categoría en la que está denegado en la interfaz de usuario.
| ID | aún sin CVE |
| Clase | Privilegio residual / omisión de autorización |
| Alcance | Remoto (token de API con core.login.api) |
| CWE | CWE-863 |
| CVSS | Medio: 6.5 CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:H/A:N |
| Producto | Joomla CMS |
| Afectado | 6.1.3 (0f2d4de4729eb951e0878fec98c7990da07915b2), POST /api/index.php/v1/content/articles |
| Autenticación | Token de API para Manager (grupo 6) con core.login.api, core.manage de com_content y core.create en una sola categoría. Sin Super User. El valor predeterminado de core.login.api es solo para Super User. |
| Licencia | GNU Affero GPL v3.0 |
| Laboratorio | solo 127.0.0.1 |
Usar un token de API que no es de Super User y que puede crear en una categoría para publicar en categorías en las que está prohibido. No se convierten en Super User a partir de este privilegio residual. Se trata de una publicación no autorizada, no de una toma de control del sitio ni de una lectura de artículos ocultos.
| Privilegio | Método / ruta | Tipo | Demostrado en laboratorio o inferido |
|---|---|---|---|
| Crear un artículo en una categoría a la que se le deniega | POST /api/index.php/v1/content/articles JSON catid de B | integridad | Demostrado en laboratorio. HTTP 200, #__content id=1, catid=9 (Press), título poc-art-JOOMLA-ARTICLES-CATID-WITNESS |
| Colocar una copia publicada en Official notices / Press mientras solo se permiten borradores | Mismo POST, state=1 | integridad | Demostrado en laboratorio la creación en Press denegada. Inferido para cualquier otro recurso de categoría con core.create denegado |
| Editar un artículo existente mediante la API | PATCH /api/index.php/v1/content/articles/{id} | n/a | Inferido cerrado para este Manager. El allowEdit base requiere el componente core.edit; las reglas de laboratorio de com_content otorgan core.edit a Author (4), no a Manager (6). El laboratorio no ejecutó PATCH |
| Eliminar un artículo mediante la API | DELETE /api/index.php/v1/content/articles/{id} | n/a | Inferido cerrado. El allowDelete base requiere el componente core.delete. El laboratorio no ejecutó DELETE |
| Guardar un artículo en la categoría permitida A desde el administrador | POST ... task=article.save jform[catid]=A | previsto | Demostrado en laboratorio abierto según lo previsto. HTTP 200, no denegado |
| Guardar un artículo en la categoría denegada B desde el administrador | Misma tarea, jform[catid]=B | n/a | Demostrado en laboratorio cerrado. HTTP 200 con "not permitted" / create-not-permitted (UI allowAdd en com_content.category.B) |
Acceder a la API con un Manager estándar (sin core.login.api) | Token Bearer / X-Joomla-Token | n/a | Inferido cerrado. El valor predeterminado de core.login.api es solo para Super User (grupo 8). Este laboratorio lo concede primero al grupo 6 |
| Misma brecha en contacts / banners / newsfeeds | POST /api/index.php/v1/contacts (y similares) con catid ajeno | integridad | Inferido. Esos controladores de API también carecen de allowAdd. El laboratorio no los ejercitó |
| RCE de Super User / instalador | n/a | n/a | Cerrado. Manager sigue siendo Manager |
Fijé la versión 6.1.3 tras la oleada de JSST de 2026-07/08. La API de Groups ya requiere core.admin. El servicio web de Media ya requiere core.edit en la sobrescritura. La API de Articles todavía pasa por ApiController y considera que "crear en cualquier categoría" es suficiente para cada catid.
public function add()
{
if (!$this->allowAdd()) {
throw new NotAllowed('JLIB_APPLICATION_ERROR_CREATE_RECORD_NOT_PERMITTED', 403);
}
$recordId = $this->save();
$this->displayItem($recordId);
}
Comprobación 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'));
}
La interfaz de usuario del administrador ya 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();
}
Falsos pasos ya registrados: leer el HTTP 200 de la UI como permitido (la denegación de B es el cuerpo "not permitted"); tratar el componente core.create como el agujero (el Manager del laboratorio no tiene ninguno; una categoría permitida es suficiente para el recuento base de getAuthorisedCategories); un Manager estándar sin core.login.api. Teatro: una reverse shell. El oráculo es API 200 en Press más UI permitida en Drafts más UI denegada en Press.
Restos del mismo pin: levels API, menu items API, media override.
python3 joomla-articles-catid-Abraxas-Labs.py
o
cd lab
./run.sh
Dirígete solo a http://127.0.0.1:19550. Imagen joomla:6.1.3-php8.3-apache más 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 es el siguiente autoincremento de #__content (1 en una instalación limpia de 6.1.3). ui-a / ui-b devuelven HTTP 200; la denegación de B es el cuerpo "not permitted" de la UI, no un estado 403.