PHP Object Injection -- Deserialization

عبداللهمنذ 3 سنوات

بسم الله الرحمن الرحيم

الحمد لله الذي أحاط بكل شيء علماً، ووسع كلَّ شيء حفظاً ثم الصلاة والسلام على الصادق الأمين نبي الرحمة والهدى صلى الله عليه وسلم تسليماً كثيراً وبعد: اللهم علمنا ما ينفعنا وانفعنا بما علمتنا وزدنا علماً إنك أنت العليم الحكيم.

في هذا المقال بإذن الله سأحاول شرح موضوع مهم في مجال إختبار إختراق تطبيقات الويب وهو ما يسمى هجوم Insecure Deserialization في لغات برمجة الويب كـ java , .Net , php  وأيضاً له اسم آخر في لغة php وهو Object Injection .

قبل أن تطرق لكيفية حدوث الهجوم يجب أن نشرح أولاً مفهوم في لغات البرمجة يسمى serialization والعملية العكسية له وهو Deserialization.

أولا serialization : بأبسط تعريف له هو عملية تحويل الكائن "Object" إلى سلسلة من البايت "Stream of bytes"

الغرض منه حفظ الكائن في ملف أو قواعد البيانات أو حتى إرساله عبر الشبكة.

ملاحظة : يتم عمل serialization في الكلاس للخصائص فقط.

 

مثال لعملية serialization في PHP

<?php
	class Car {
		var $type;
		var $speed;
	}

	$obj = new Car();
	echo serialize($obj);
?>

الناتج :

O:3:"Car":2:{s:4:"type";N;s:5:"speed";N;}

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

O : يرمز لـ Object حيث أننا قمنا بعمل serialization لكائن. بعدها نقطتين رأسيتين : حيث تستخدم للفصل بين النوع والطول والاسم ... الخ

3: طول اسم الكلاس يساوي 3 "Car" فلو مثلاً كان اسم الكلاس "Human" فسيكون الطول 5 حيث أن عدد الأحرف 5 وهكذا.

Car : كما هو واضح اسم الكائن أو بالأصح اسم الكلاس

2: عدد الخصائص في الكلاس , كما في مثالنا يوجد لدينا متغيرين type & speed فلو كان لدينا متغير ثالث color فهذه تصبح ثلاثة وهكذا.

{}: بين هذين القوسين نضع خصائص الكلاس ونمثلها كما مثلنا الكائن في الأعلى.

s: مثلما قلنا في الأعلى أن O ترمز للكائن فهنا s ترمز للنص "string" وهناك أنواع أخرى مثل: strings, objects, arrays, integers, NULL .

4: طول أسم المتغير "type" ويساوي 4 .

type: اسم المتغير وبعدها ; ثم قيمة المتغير ونلحظ هنا أنها N حيث أننا لم نضع قيمة له في الكلاس فأصبح Null.

            مثال لو فرضنا أن المتغير كانت قيمته تساوي "ABC" فعندها بدلاً من N تصبح "s:3:"ABC

    ملاحظة: بعد تعريف متغير أو قيمة نضع فاصلة منقوطة ;

وهكذا نكمل تعريف بقية المتغيرات وقيمها.

- دالة ()serialize في PHP هي اللتي تقوم بعملية serialization للكائن.

 

الآن بمعرفتنا ب serialization نعرف أن deserialization هي العملية المعاكسة لها وهي باختصار : إعادة تحويل سلسة من البايت إلى كائن

 

الآن من المثال السابق سنقوم بعمل deserialization للكائن Car الذي قمنا بعمل serialization له

<?php
	class Car {
		var $type;
		var $speed;
	}

	$obj = new Car();
	$des_obj = unserialize(serialize($obj));
	var_dump($des_obj);
	
?>

الناتج :

object(Car)#2 (2) {
  ["type"]=>
  NULL
  ["speed"]=>
  NULL
}

أيضاً هنا المطلوب أن تعلم أن unserialize هي اللتي تستخدم في PHP.

 

لنتكلم الآن كيف يحدث الهجوم :

باختصار يحدث هذا الهجوم عندما نمرر قيمة مأخوذة من المستخدم من دون عمل فلترة لها لدالة ()unserialize

الآن نظراً لأن الهجوم يستهدف عملية deserialization فيجب علينا أن نعرف مالذي يحدث فعلاً عندما نقوم باستدعاء دالة ()unserialize :

عندما نمرر serialized data إلى دالة ()unserialize تقوم الدالة بإنشاء الكائن وإسناد القيم في هذا الكائن وعندها يصبح لدينا الكائن جاهز وتنفذ عندها الدوال السحرية "Magic Methods"

لكن يوجد شرطان يجب توفرهما ليتمكن المهاجم من إستغلال هذه الثغرة:

1- التطبيق يجب أن يكون فيه كلاس يمرر قيمة مدخلات المستخدم إلى أحد الدوال السحرية "Magic Methods" في PHP التي بدورها تستدعي دالة لتنفيذ المدخلات من المستخدم.

2- كل الكلاسات المستخدمة عند الهجوم ويتم تمريرها لدالة unserialize يجب أن مستدعاة أو معرفة قبل استدعاء هذه الدالة.

 

مالمقصود بالدوال السحرية Magic Methods ؟

هي باختصار دوال يتم استدعاؤها تلقائياً أثناء مرحلة معينة من الكلاس وتبدأ أسماء هذه الدوال في PHP بشرطتين سفليتين __ مثال:

__wakeup(),  __construct(), __destruct(), __call(),

مثلاً دالة  __construct() تستدعى عند إنشاء كائن من هذا الكلاس ودالة __destruct() عند الإنتهاء من استخدام الكائن.

 

لنشرح هذا الكلام بهذا المثال : لنفرض أن لدينا تطبيق فيه كلاسين كلاس واحد اسمه CommandExecute وهذا يقوم بأخذ قيمة cmd وفي دالة __destruct() يقوم بتنفيذ الأمر :

<?php
...
    class CommandExecute {
        public $cmd;

        function __destruct(){
            
            if (isset($this->cmd)){
                system($this->cmd);
            }
        }
    }

    class UserInfo {
        public $id;
        public $name;
        public $desc;
    }
...
?>

طبعاً لا يفترض أن المستخدم أن يصل إلى هذا الكلاس فلذلك يتم استخدام هذا الكلاس عن طريق المبرمج فقط في ملف php دون أخذ قيمة من المستخدم.

إستكمالاً لمثالنا والكلاس الآخر UserInfo يكون لحفظ وعرض بيانات المستخدم, البرنامج هنا سيقوم بأخذ serialized data من المستخدم عن طريق GET pararmeter بدون فلترة للمدخلات.

طبعاً الخطأ هنا أن المبرمج توقع أن البيانات ستأتي صحيحة على الشكل التالي :

O:8:"UserInfo":3:{s:2:"id";N;s:4:"name";N;s:4:"desc";N;}

لكن بما أنه لا يفلتر المدخلات يمكننا تغييرها لاحظ الكود التالي :

<?php 
...

$obj = unserialize($_GET['data']);
# ... do what ever with obj... 
?>

الآن كل ما علينا فعله كمخترقين بدلاً من تمرير بيانات كلاس UserInfo نستبدلها بكلاس CommandExecute مع قيم لمتغيراته:

                   http://VulnWebsiteExample/vuln.php?data=O:14:"CommandExecute":1:{s:3:"cmd";s:6:"whoami";}    

فعندما يقوم البرنامج بعمل deserialization لهذا سيقوم باستدعاء كلاس CommandExecute ووضع قيمة للمتغير cmd الذي يتم تنفيذه في دالة destruct بعد الإنتهاء من الكائن ! هنا حصل RCE

 

Gadegts

الآن فهمنا كيف يحدث وكيف نستغله لكن هناك سؤال مهم كيف نعرف ما هو الكلاس الموجود الذي نستطيع استغلاله ؟ وكيف نستغله ؟ هنا يأتي مصطلح مهم في ثغرات deserialization وهو "Gadget" وترجمتها الحرفية أداة وفي الإنقليزية تعريفها هي "الاداة الذكية أو المبتكرة" على العموم لا أريد الإطالة في معناها الحرفي :) ولكن لنعرف معناها التقني مع deserialization.

هي مجموعة من الكائنات والخصائص التي تستدعي بعضها البعض مكونة لنا سلسلة من الإستدعاءات مكونة لنا في النهاية تنفيذ لأمر معين. طبعاً لها نقطة بداية ولها نقطة نهاية.

مثال على ذلك : لو كان عندنا كلاس اسمه A وكلاس آخر اسمه B , كلاس A لديه ثلاث خصائص : اسم الكلاس واسم الدالة والقيم التي نريد تمريرها للدالة ويقوم باستدعاء الدالة من الكلاس الذي مررنا قيمته له في إحدى الدوال السحرية. كلاس B لديه دالة اسمها exec تاخذ قيمة ويقوم باستدعائها داخل إحدى الدوال السحرية. هنا باختصار Gadget سيكون كلاس A والذي بداخله يتم إستدعاء كلاس B .

 

كما قلنا سابقاً أن عملية deserialization لها شرطين ومن ضمنها أن الكلاس يكون معرف مسبقاً سيتضح لنا صعوبة إيجاد gadets المناسبة لنا وطبعاً اكتشاف gadets جديد ليس هدفنا فلذلك نستخدم أدوات جاهزة لمثل هذا الأمر مثل PHPGCC .

 

مالذي يمكن عمله في هذا الهجوم ؟

تعتمد على الثغرة نفسها فقد تسبب RCE , XSS, Authentication bypass ...etc

 

كيفية الحماية منها ؟

هناك عدة طرق للحماية منها من ضمنها عدم استخدام دالة ()unserialize مع المدخلات من المستخدم , منع RCE , التحقق من القيم التي تم عمل serialized لها وغيرها.

 

في الختام أرجو من الله أن أكون وفقت في إيصال المعلومة بأبسط صورة إليك أيها القارئ الكريم :)

إن أصبت فمن الله وحده وإن أخطأت فمن نفسي والشيطان وأرجو منكم التصحيح..

والسلام عليكم ورحمة الله وبركاته

 

المراجع:

https://owasp.org/www-community/vulnerabilities/PHP_Object_Injection

https://blog.ripstech.com/2018/php-object-injection/

https://nitesculucian.github.io/2018/10/05/php-object-injection-cheat-sheet/

https://www.ixiacom.com/company/blog/exploiting-php-phar-deserialization-vulnerabilities-part-1

https://www.youtube.com/watch?v=HaW15aMzBUM

 

0
إعجاب
2134
مشاهدات
0
مشاركة
1
متابع

التعليقات (0)

لايوجد لديك حساب في عالم البرمجة؟

تحب تنضم لعالم البرمجة؟ وتنشئ عالمك الخاص، تنشر المقالات، الدورات، تشارك المبرمجين وتساعد الآخرين، اشترك الآن بخطوات يسيرة !