Skip to content
KitploitKITPLOIT
أدواتالمدونة
إرسال
أدواتالمدونة
إرسال

أدوات الاختراق واختبار الاختراق والأمن السيبراني لترسانتك الأمنية!

Kitploit هو دليل لأدوات الاختراق والأمن السيبراني واختبار الاختراق. اكتشف آخر تحديثات المشاريع للعثور على الثغرات وتحليل الأنظمة وأتمتة الاختبارات وتعزيز أمنك.

··الخلاصات·اتصال·الخصوصية·© 2026 Kitploit

دليل الأدوات

الفئات

عرض جميع الفئات
Loading categories
CVE-2019-11043 — استغلال تنفيذ الأوامر عن بُعد في PHP-FPM | Kitploit
أدوات/GitHubGitHub/lindemer/cve-2019-11043
تحليل الثغرات الأمنيةالاستغلالاستغلال تطبيقات الويباختبار الاختراقالتعلم والتعليمتطوير الحمولات
GitHublindemer/cve-2019-11043

CVE-2019-11043

استغلال تنفيذ الأوامر عن بُعد في PHP-FPM

عرض المستودع
42منذ 5 سنواتلم تتم المراجعة بعد

الأكثر شعبية

عرض الكل →

اكتشف الأدوات الأكثر استخدامًا من قبل مجتمعنا.

استكشف جميع الأدوات

تصفح مجموعتنا من الأدوات

عرض جميع الأدوات →
مشاركة

CVE-2019-11043

تنفيذ كود عن بُعد في PHP-FPM

Screencast: https://youtu.be/d6benC5FVZM

نظرة عامة

اكتُشف هذا الاستغلال من نوع zero-day في إعدادات PHP-FPM الشائعة خلال مسابقة Realworld CTF في عام 2019. يُستخدم تعبير منتظم لتحليل URI المطلوب، لكن أحرف السطر الجديد %0a لا تُطابَق. يؤدي ذلك إلى إثارة خلل في FastCGI يحسب طول سلسلة الاستعلام بشكل غير صحيح ويكتب بايتًا صفريًا في موقع قبل بداية المخزن المؤقت المقصود. من خلال اختيار دقيق لطول سلسلة الاستعلام، يمكن للمهاجم استغلال هذا الخلل لاستبدال متغيرات PHP الداخلية على الخادم وتنفيذ أوامر شل عشوائية.

يمكن العثور على التنفيذ الأصلي لهذا الاستغلال بلغة Go في هذا الرابط. استخدمت هذا، ومقالًا تحليليًا، وتقرير الخلل الأصلي كمراجع تعليمية من أجل تنفيذ الاستغلال بلغة Python.

التعليمات

Docker على لينكس نفّذ الأمر sudo docker run --rm -ti -p 8080:80 reproduce-cve-2019-11043 لإنشاء خادم NGINX/PHP-FPM بسيط مع سكربت فارغ في /script.php. ملف Dockerfile لهذه الصورة متاح هنا، مع أنّه غير مطلوب لتشغيل الأمر المذكور أعلاه.

Docker على ماك نفّذ sudo docker-compuse up -d من المجلد /php/CVE-2019-11043 في مستودع vulhub. (Compose مضمّن مع Docker for Mac).

شغّل سكربت الاستغلال بالأمر python3 exploit.py http://localhost:8080/script.php (أو /index.php إذا استُخدم الخيار الثاني). بعد التنفيذ الناجح، سيكون الوصول إلى Web shell ممكنًا عبر إلحاق الأوامر بعنوان URL بعد ?a= (مثل: http://localhost:8080/script.php?a=uname -a).

ملاحظة: لقد حاولت إنشاء playbook بـ Ansible لهذه المهمة، لكنني اصطدمت بخلل يوقف العمل موثّق هنا. من غير الممكن بدء خدمات systemd على أنوية لينكس الحديثة (مثل أي إصدار Ubuntu LTS) باستخدام playbook من Ansible.

النظرية

الثغرة

تحتوي ملفات إعدادات PHP-FPM على قاعدة لمطابقة طلبات URI الواردة مع سكربتات PHP، وغالبًا ما تبدو كما يلي:

root@kitploit:~
location ~ [^/]\.php(/|$) {
  ...
  fastcgi_split_path_info       ^(.+?\.php)(/.*)$;
  fastcgi_param PATH_INFO       $fastcgi_path_info;
  fastcgi_pass                  php:9000;
  ...
}

من المفترض أن تطابق هذه القاعدة أي URI بالشكل /script.php/pathinfo، لكن . في الواقع لا يطابق أحرف السطر الجديد %0a. إذا احتوى URI على سطر جديد، فسيؤدي ذلك إلى إثارة الخلل التالي في تنفيذ PHP:

root@kitploit:~
1141    int ptlen = strlen(pt);
1142    int slen = len - ptlen;
1143    int pilen = env_path_info ? strlen(env_path_info) : 0;
1144    int tflag = 0;
1145    char *path_info;
1146    if (apache_was_here) {
1147        /* recall that PATH_INFO won't exist */
1148        path_info = script_path_translated + ptlen;
1149        tflag = (slen != 0 && (!orig_path_info || strcmp(orig_path_info, path_info) != 0));
1150    } else {
1151        path_info = env_path_info ? env_path_info + pilen - slen : NULL;
1152        tflag = (orig_path_info != path_info);
1153    }

المشكلة هنا هي أن slen يُحسب بشكل صحيح كطول URI ناقص طول مسار المورد، لكن pilen يُضبط خطأً على 0. يؤدي ذلك إلى جعل path_info يساوي قيمة سالبة في السطر 1151، مما ينتج عنه تجاوز للمخزن المؤقت نحو الأسفل (buffer underflow). بعد هذا الحساب الخاطئ مباشرةً في الملف نفسه، لدينا:

root@kitploit:~
1159    FCGI_PUTENV(request, "ORIG_PATH_INFO", orig_path_info);
1160    old = path_info[0];
1161    path_info[0] = 0;
1162    if (!orig_script_name ||
1163        strcmp(orig_script_name, env_path_info) != 0) {
1164        if (orig_script_name) {
1165            FCGI_PUTENV(request, "ORIG_SCRIPT_NAME", orig_script_name);
1166        }
1167        SG(request_info).request_uri = FCGI_PUTENV(request, "SCRIPT_NAME", env_path_info);
1168    } else {
1169        SG(request_info).request_uri = orig_script_name;
1170    }
1171    path_info[0] = old;

في السطر 1161، يُكتب بايت صفري إلى موقع الذاكرة الذي حُسب بشكل خاطئ في الخطوة السابقة. يمكن استغلال ذلك لاستهداف ثغرة في السطر 1165، حيث يكتب FastCGI متغير بيئة. بكتابة البايت الصفري داخل المؤشر الذي يتحكم في عملية كتابة متغير البيئة، يمكننا إدراج متغيرات PHP عشوائية في البيئة عبر طلبات HTTP الخاصة بنا.

الاستغلال

البنى الداخلية للبيانات في FastCGI

تُخزَّن متغيرات البيئة في FastCGI في تسلسل متراص بإحكام من أزواج مفاتيح-قيم على شكل سلاسل نصية في الذاكرة. تُسمى بداية ونهاية المخزن المؤقت الذي يحمل هذه السلاسل _fcgi_data_seg. العضو pos يشير إلى الموقع المتاح التالي للكتابة. إذا امتلأ المخزن (pos > end)، يُخصص مخزن جديد ويشير العضو next إلى المخزن القديم.

root@kitploit:~
118    typedef struct _fcgi_data_seg {
119        char                  *pos;
120        char                  *end;
121    	   struct _fcgi_data_seg *next;
122    	   char                   data[1];
123    } fcgi_data_seg;

يصل FastCGI إلى متغيرات البيئة الفردية باستخدام جدول تجزئة يسمى _fcgi_hash.

root@kitploit:~
125    typedef struct _fcgi_hash {
126    	   fcgi_hash_bucket  *hash_table[FCGI_HASH_TABLE_SIZE];
127    	   fcgi_hash_bucket  *list;
128        fcgi_hash_buckets *buckets;
129        fcgi_data_seg     *data;
130    } fcgi_hash;

الفكرة هنا هي استبدال البايت الأقل أهمية من pos بهدف خداع FastCGI ليستبدل متغيرًا موجودًا بالفعل. من المفترض أن يأخذ الكود السلسلة الملحقة بمسار URI ويضعها في الموقع الخاص بـ PATH_INFO. لكننا نريد استبدال PHP_VALUE، لأن هذه القيمة تُسترد فورًا وتُحمَّل في إعدادات PHP بعد مقطع الكود المعرّض للثغرة.

محاذاة البيانات

كما ترى في exploit.py، الفرضية العامة لهذا الاستغلال هي إيجاد استعلام URI طويل جدًا يُحاذي المخزن المؤقت الداخلي لـ FastCGI بطريقة يمكننا استغلالها. الفكرة هي إيجاد العدد الدقيق للأحرف المطلوب لدفع FastCGI إلى تخصيص مخزن _fcgi_data_seg جديد. عند حدوث ذلك، سيكتب FastCGI بشكل متوقع PATH_INFO في المخزن الجديد، يليه مباشرةً كل ترويسات HTTP الخاصة بنا كقيم بيئة جديدة. لذا، الخطوة التالية هي معرفة عدد الأحرف التي نحتاج إلى حشو ترويسة HTTP عشوائية بها لمحاذاة الذاكرة وفق أغراضنا. وبما أننا مقيدون بكتابة بايت صفري واحد في موقع عشوائي، نحتاج إلى جعل pos يشير إلى إزاحة يمكن التنبؤ بها بالنسبة إلى PHP_VALUE بحيث يؤدي تعديل البايت الأقل أهمية إلى تحريكه إلى هناك.

تجاوز جدول التجزئة

التحدي هو أننا نريد استبدال PHP_VALUE، لكننا لا نعرف أين يقع هذا المتغير في الذاكرة. عندما يحمّل FastCGI هذا المتغير، فإنه يجزئ السلسلة PHP_VALUE للحصول على عنوان الذاكرة الفعلي وفقًا لخوارزمية بسيطة:

root@kitploit:~
31    #define FCGI_HASH_FUNC(var, var_len) \
32        (UNEXPECTED(var_len < 3) ? (unsigned int)var_len : \
33        (((unsigned int)var[3]) << 2) + \
34        (((unsigned int)var[var_len-2]) << 4) + \
35        (((unsigned int)var[var_len-1]) << 2) + \
36        var_len)

بدلاً من تعديل جدول التجزئة نفسه بطريقة أو بأخرى، كل ما علينا فعله هو إنشاء متغير بيئة آخر له نفس طول السلسلة ونفس قيمة التجزئة مثل PHP_VALUE وفقًا لهذه الدالة. سيؤدي ذلك إلى خداع عملية البحث في التجزئة لتقرأ ترويسة HTTP الخاصة بنا بدلاً من المتغير المقصود. لاحظ مؤلف هذا الاستغلال بذكاء أن ترويسة اسمها EBUT ستُحفظ باسم HTTP_EBUT في بيئة FastCGI، وهو ما يحقق هذا الشرط.

حقن الكود

بالنسبة للهجوم نفسه، نرسل طلبات GET تحتوي على ترويسة EBUT الخاصة بنا ونستخدم خلل استبدال البايت الصفري لاستبدال قيمتها. نحاول ضبط متغيرات بيئة PHP واحدًا تلو الآخر عبر طلبات متكررة:

root@kitploit:~
short_open_tag=1
html_errors=0
include_path=/tmp
auto_prepend_file=a
log_errors=1
error_reporting=2
error_log=/tmp/a
extension_dir=\"<?=`\"
extension=\"$_GET[a]`?>\"

يؤدي التعديل الناجح لجميع هذه المتغيرات إلى تمكين استعلام جديد ?a= على الخادم لتنفيذ أكواد شل عشوائية. تتحقق حلقة الهجوم من النجاح في كل تكرار من خلال محاولة تنفيذ which which. يمكن للمهاجم بسهولة اكتشاف ما إذا كان ذلك ناجحًا عبر قراءة النتيجة من استجابة HTTP (مثل /bin/which).

تنزيل الأداة