
Joomla 6.1.3 - Alta - Remota - ACL - POST /api/v1/content/articles ignora l'ACL per categoria
abraxaslabs.tech · github.com/abraxas · @abraxas_null · [email protected] · joomla-articles-catid
Write-up: https://abraxaslabs.tech/research/joomla-articles-catid
Classe: Privilegio residuo Portata: Remota
Joomla CMS 6.1.3 (0f2d4de) - Open Source Matters
Il metodo amministratore ArticleController::allowAdd verifica com_content.category.{catid}. api/components/com_content/src/Controller/ArticlesController.php non ha allowAdd. ApiController::add() chiama quindi allowAdd() senza dati, per cui il catid JSON non entra mai nel controllo. Il metodo base allowAdd restituisce true se l'utente può creare in qualsiasi categoria.
Un utente API di classe Manager, a cui sono stati concessi core.login.api e la creazione su una categoria, può pubblicare tramite POST un articolo in una categoria in cui è negato nell'interfaccia utente.
| ID | nessuna CVE al momento |
| Classe | Privilegio residuo / bypass dell'autorizzazione |
| Portata | Remota (token 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 |
| Prodotto | Joomla CMS |
| Affetto | 6.1.3 (0f2d4de4729eb951e0878fec98c7990da07915b2), POST /api/index.php/v1/content/articles |
| Autenticazione | Token API per Manager (gruppo 6) con core.login.api, com_content core.manage e core.create su una sola categoria. Nessun Super User. Il valore predefinito di core.login.api è riservato al solo Super User. |
| Licenza | GNU Affero GPL v3.0 |
| Lab | solo 127.0.0.1 |
Usare un token API non Super User che può creare in una categoria per pubblicare in categorie da cui è bandito. Non diventa Super User grazie a questo privilegio residuo. Si tratta di pubblicazione non autorizzata, non di un takeover del sito e non della lettura di articoli nascosti.
| Privilegio | Metodo / percorso | Tipo | Provato in laboratorio o dedotto |
|---|---|---|---|
| Creare un articolo in una categoria a cui è negato l'accesso | POST /api/index.php/v1/content/articles JSON catid di B | integrità | Provato in laboratorio. HTTP 200, #__content id=1, catid=9 (Press), titolo poc-art-JOOMLA-ARTICLES-CATID-WITNESS |
| Inserire una copia pubblicata in Official notices / Press mentre sono consentite solo Bozze | Stesso POST, state=1 | integrità | Provato in laboratorio: creazione in Press negata. Dedotto per qualsiasi altra risorsa di categoria con core.create negato |
| Modificare un articolo esistente tramite l'API | PATCH /api/index.php/v1/content/articles/{id} | n/d | Dedotto: chiuso per questo Manager. La base allowEdit richiede core.edit del componente; le regole di laboratorio di com_content assegnano core.edit ad Author (4), non a Manager (6). Il laboratorio non ha eseguito PATCH |
| Eliminare un articolo tramite l'API | DELETE /api/index.php/v1/content/articles/{id} | n/d | Dedotto: chiuso. La base allowDelete richiede core.delete del componente. Il laboratorio non ha eseguito DELETE |
| Salvare un articolo nella categoria consentita A dall'amministratore | POST ... task=article.save jform[catid]=A | previsto | Provato in laboratorio: aperto come previsto. HTTP 200, non negato |
| Salvare un articolo nella categoria negata B dall'amministratore | Stessa task, jform[catid]=B | n/d | Provato in laboratorio: chiuso. HTTP 200 con "not permitted" / create-not-permitted (UI allowAdd su com_content.category.B) |
Chiamare l'API con un Manager standard (senza core.login.api) | Bearer token / X-Joomla-Token | n/d | Dedotto: chiuso. Il valore predefinito di core.login.api è solo Super User (gruppo 8). Questo laboratorio lo concede prima al gruppo 6 |
| Stessa lacuna su contacts / banners / newsfeeds | POST /api/index.php/v1/contacts (e simili) con catid estraneo | integrità | Dedotto. Anche quei controller API mancano di allowAdd. Il laboratorio non li ha testati |
| RCE da Super User / installer | n/d | n/d | Chiuso. Il Manager resta Manager |
Ho fissato la versione 6.1.3 dopo l'ondata JSST di 2026-07/08. L'API Groups richiede già core.admin. Il webservice Media richiede già core.edit sull'override. L'API Articles continua invece a passare per ApiController e considera "creare in qualsiasi categoria" sufficiente per ogni catid.
public function add()
{
if (!$this->allowAdd()) {
throw new NotAllowed('JLIB_APPLICATION_ERROR_CREATE_RECORD_NOT_PERMITTED', 403);
}
$recordId = $this->save();
$this->displayItem($recordId);
}
Controllo di 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'));
}
L'interfaccia utente di amministrazione si basa già su 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();
}
Percorsi errati già registrati: leggere l'HTTP 200 dell'interfaccia utente come autorizzazione (il diniego di B è il corpo "not permitted"); trattare il componente core.create come la falla (il Manager del lab non ne ha nessuno; una sola categoria consentita è sufficiente per il conteggio di base di getAuthorisedCategories); un Manager standard senza core.login.api. Teatro: una reverse shell. L'oracolo è l'API 200 in Press più l'autorizzazione dell'interfaccia utente su Drafts più il diniego dell'interfaccia utente su Press.
Residui dello stesso pin: levels API, menu items API, media override.
python3 joomla-articles-catid-Abraxas-Labs.py
o
cd lab
./run.sh
Prendi di mira solo http://127.0.0.1:19550. Immagine joomla:6.1.3-php8.3-apache più 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 è il successivo autoincrement di #__content (1 su un'installazione pulita di 6.1.3). ui-a / ui-b restituiscono HTTP 200; il rifiuto di B è il corpo "not permitted" dell'interfaccia, non uno stato 403.