
إثبات المفهوم لاستغلال CVE-2025-49132 الذي يتيح تنفيذ التعليمات البرمجية عن بُعد دون مصادقة في لوحة Pterodactyl الإصدار 1.11.10 وما دونه عبر حقن معامل اللغة. يتضمن وضعي الفحص والاستغلال.
برهان المفهوم لـ CVE-2025-49132 لتنفيذ الأوامر عن بُعد في Pterodactyl Panel <= 1.11.10
يستعين هذا البرهان بالمصادر التالية:
# Check if the web application is vulnerable
python3 poc.py -u <url> -m check
# Do something funny
python3 poc.py -u <url> -m exploit -p <path_to_pear_dir> -t <path_to_write_to>
بتحليل git diff للملف LocaleController.php بين الإصدارين v1.11.10 و v1.11.11 (رابط)، نلاحظ أن المتحكم يقبل المعاملات locale و namespace دون أي تحقق أو تعقيم.
لفهم سبب خطورة ذلك، سننظر إلى التوجيه المحدد في panel/routes/base.php. يزيل المسار صراحةً الوسائط المتعلقة بالمصادقة، مما يسمح للمستخدمين غير المصادق عليهم باستدعاء /locales/locale.json وتوفير معاملات الاستعلام المطلوبة مثل locale و namespace. هذا يعني أن هذه المعاملات أصبحت تحت سيطرة المستخدم.
Route::get('/locales/locale.json', Base\LocaleController::class)
// Remove authentication middleware for this endpoint
->withoutMiddleware(['auth', RequireTwoFactorAuthentication::class]);
بالعودة إلى LocalController.php، يوفر السطر التالي من الكود سحر تنفيذ الأوامر عن بُعد.
$this->loader->load($locale, str_replace('.', '/', $namespace))
تأتي الدالة load من إطار الترجمة الخاص بـ Laravel (هنا):
public function load($locale, $group, $namespace = null) # 1
{
if ($group === '*' && $namespace === '*') { # 2
return $this->loadJsonPaths($locale);
}
if (is_null($namespace) || $namespace === '*') { # 3
return $this->loadPaths($this->paths, $locale, $group); # 4
}
return $this->loadNamespaced($locale, $group, $namespace);
}
load تأخذ 3 وسائط. على الرغم من أنه مربك في البداية، فإن القيمة str_replace('.', '/', $namespace) تُمرر في الواقع إلى الوسيط الثاني $group.$namespace أبدًا * لأنه سيكون دائمًا NULL. وبالمثل، لن يكون $group أبدًا * لأننا نتحكم في ما يُمرر هنا.$namespace هو null، سيتم أخذ الفرع مما يؤدي إلى تنفيذ loadPaths عند #4.بفهم ما سبق، ننتقل إلى تحليل loadPaths حيث يحدث السحر الفعلي
protected function loadPaths(array $paths, $locale, $group)
{
return (new Collection($paths))
->reduce(function ($output, $path) use ($locale, $group) {
if ($this->files->exists($full = "{$path}/{$locale}/{$group}.php")) { # 1
$output = array_replace_recursive($output, $this->files->getRequire($full)); # 2
}
return $output;
}, []);
}
.php.getRequire()، التي تنفذ ملف PHP وتعيد مصفوفته.نظرًا لأننا نتحكم في معامل عنوان URL namespace، يمكننا التأثير على الملف الذي يحاول التطبيق تحميله. من خلال توفير قيمة تتوافق مع سكربت .php، يمكننا جعل الخادم ينفذ ذلك السكربت. على سبيل المثال، لتحميل phpinfo.php، يمكننا ضبط المعامل على: namespace=phpinfo
يحاول البرهان إجراء كتابة عشوائية للملفات عبر pearcmd.php. تم استخدام هذه الطريقة في العديد من البراهين والأبحاث الأمنية المماثلة
لاحظ أن pearcmd.php يمكن أن يوجد في أدلة مختلفة مثل
/usr/share/php/pearcmd.php/usr/share/php/PEAR/pearcmd.php/usr/lib/php/pearcmd.phpوغيرها الكثير.
سيحتوي الملف الذي يتم كتابته على كود PHP ضار نموذجي.
للاستخدام المأذون به فقط. لا تستخدم على أنظمة لا تملكها أو لا تملك إذنًا صريحًا لاختبارها.
أنت مسؤول عن الامتثال لجميع القوانين المعمول بها؛ يفترض المؤلفون عدم تحمل أي مسؤولية عن سوء الاستخدام.
مكتبة requests في Python مفيدة جدًا لدرجة أنها تقوم بترميز الأحرف في عنوان URL المقدم. قضيت وقتًا طويلاً في محاولة حل هذه المشكلة وانتهى بي الأمر باستخدام curl مباشرة. سأكون ممتنًا لو تمكن أي شخص من تقديم بعض المساعدة بخصوص هذه المشكلة.