
Joomla 6.1.3 - عالي - عن بُعد - ACL - POST /api/v1/content/articles يتجاهل ACL الخاص بكل فئة
abraxaslabs.tech · github.com/abraxas · @abraxas_null · [email protected] · joomla-articles-catid
الفئة: صلاحية متبقية الوصول: عن بُعد
Joomla CMS 6.1.3 (0f2d4de) - Open Source Matters
المدير ArticleController::allowAdd يتحقق من com_content.category.{catid}. api/components/com_content/src/Controller/ArticlesController.php لا يحتوي على allowAdd. ثم يستدعي ApiController::add() الدالة allowAdd() بدون بيانات، لذا لا يدخل catid الخاص بـ JSON في التحقق أبدًا. الدالة الأساسية allowAdd تكون صحيحة إذا كان المستخدم قادرًا على الإنشاء في أي فئة.
مستخدم API من فئة Manager، مُمنَح core.login.api وصلاحية الإنشاء على فئة واحدة، يمكنه إرسال مقال عبر POST إلى فئة ممنوع منها في واجهة المستخدم.
| المعرّف | لا يوجد CVE بعد |
| الفئة | صلاحية متبقية / تجاوز التفويض |
| الوصول | عن بُعد (رمز API مع core.login.api) |
| CWE | CWE-863 |
| CVSS | متوسط: 6.5 CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:H/A:N |
| المنتج | Joomla CMS |
| المتأثر | 6.1.3 (0f2d4de4729eb951e0878fec98c7990da07915b2)، POST /api/index.php/v1/content/articles |
| المصادقة | رمز API لـ Manager (المجموعة 6) مع core.login.api وcom_content core.manage وcore.create على فئة واحدة فقط. بدون Super User. القيمة الافتراضية لـ core.login.api هي Super User فقط. |
| الترخيص | GNU Affero GPL v3.0 |
| المختبر | 127.0.0.1 فقط |
استخدام رمز API لمستخدم غير Super User يمكنه الإنشاء في فئة واحدة للنشر في فئات محظور منها. لا يصبح Super User من هذه الصلاحية المتبقية. هذا نشر غير مصرّح به، وليس استيلاءً على الموقع ولا قراءة لمقالات مخفية.
| الصلاحية | الطريقة / المسار | النوع | مُثبَت مختبريًا أو مُستنتَج |
|---|---|---|---|
| إنشاء مقال في فئة ممنوع منها | POST /api/index.php/v1/content/articles مع catid الخاص بـ B في JSON | سلامة | مُثبَت مختبريًا. HTTP 200، #__content id=1، catid=9 (Press)، العنوان poc-art-JOOMLA-ARTICLES-CATID-WITNESS |
| زرع نسخة منشورة في Official notices / Press بينما المسموح فقط هو Drafts | نفس POST، state=1 | سلامة | مُثبَت مختبريًا الإنشاء في Press الممنوعة. مُستنتَج لأي أصل فئة آخر مع رفض core.create |
| تعديل مقال موجود عبر API | PATCH /api/index.php/v1/content/articles/{id} | لا ينطبق | مُستنتَج أنه مغلق لهذا Manager. الدالة الأساسية allowEdit تحتاج core.edit على المكوّن؛ قواعد com_content في المختبر تمنح core.edit لـ Author (4)، وليس Manager (6). المختبر لم ينفّذ PATCH |
| حذف مقال عبر API | DELETE /api/index.php/v1/content/articles/{id} | لا ينطبق | مُستنتَج أنه مغلق. الدالة الأساسية allowDelete تحتاج core.delete على المكوّن. المختبر لم ينفّذ DELETE |
| حفظ مقال في الفئة المسموح بها A من لوحة الإدارة | POST ... task=article.save مع jform[catid]=A | مقصود | مُثبَت مختبريًا مفتوح كما هو مقصود. HTTP 200، غير مرفوض |
| حفظ مقال في الفئة الممنوعة B من لوحة الإدارة | نفس المهمة، jform[catid]=B | لا ينطبق | مُثبَت مختبريًا مغلق. HTTP 200 مع "not permitted" / create-not-permitted (واجهة المستخدم allowAdd على com_content.category.B) |
استهداف API بـ Manager افتراضي (بدون core.login.api) | Bearer token / X-Joomla-Token | لا ينطبق | مُستنتَج أنه مغلق. القيمة الافتراضية لـ core.login.api هي Super User (المجموعة 8) فقط. هذا المختبر يمنحها للمجموعة 6 أولًا |
| نفس الثغرة في contacts / banners / newsfeeds | POST /api/index.php/v1/contacts (وما شابهها) مع catid أجنبي | سلامة | مُستنتَج. تلك المتحكمات في API تفتقر أيضًا إلى allowAdd. المختبر لم يختبرها |
| Super User / RCE عبر المثبّت | لا ينطبق | لا ينطبق | مغلق. Manager يبقى Manager |
ثبّتُ 6.1.3 بعد موجة JSST في 2026-07/08. Groups API تتطلب بالفعل core.admin. Media webservice تتطلب بالفعل core.edit على override. Articles API لا تزال تمر عبر ApiController وتعتبر "الإنشاء في أي فئة" كافيًا لكل catid.
public function add()
{
if (!$this->allowAdd()) {
throw new NotAllowed('JLIB_APPLICATION_ERROR_CREATE_RECORD_NOT_PERMITTED', 403);
}
$recordId = $this->save();
$this->displayItem($recordId);
}
التحقق الأساسي:
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'));
}
واجهة المدير تعتمد بالفعل على 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();
}
المنعطفات الخاطئة المسجّلة بالفعل: قراءة HTTP 200 من واجهة المستخدم كإذن (رفض B هو جسم "not permitted")؛ اعتبار core.create على المكوّن هو الثغرة (Manager في المختبر لا يملكها؛ إذن فئة واحدة يكفي لعدّ getAuthorisedCategories الأساسي)؛ Manager افتراضي بدون core.login.api. مسرحية: reverse shell. الدليل هو API 200 في Press مع إذن واجهة المستخدم على Drafts ورفض واجهة المستخدم على Press.
بقايا نفس التثبيت: levels API، menu items API، media override.
python3 joomla-articles-catid-Abraxas-Labs.py
أو
cd lab
./run.sh
الهدف فقط http://127.0.0.1:19550. الصورة joomla:6.1.3-php8.3-apache مع 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 هو القيمة التالية للتزايد التلقائي في #__content (1 على تثبيت 6.1.3 نظيف). ui-a / ui-b هما HTTP 200؛ رفض B هو جسم "not permitted" في واجهة المستخدم، وليس حالة 403.
تجاوز allowAdd (وallowEdit) في api/components/com_content/src/Controller/ArticlesController.php بنفس طريقة ArticleController في لوحة الإدارة: التحقق من core.create على com_content.category.{catid}. مرّر JSON الطلب إلى ApiController::add() بحيث يصل catid فعليًا إلى ذلك التحقق. طبّق نفس النمط على contacts وbanners وnewsfeeds إذا بقيت تلك المتحكمات على allowAdd الافتراضية.