Insecure Deserialization
Insecure Deserialization
أمور يجب عليك معرفتها قبل قراءة هذه المقالة:
- معرفة بسيطة بلغة جافا (المثال في هذه المقالة مُستند على هذه اللغة )
السلام عليكم ورحمة الله وبركاته ،
هذه المقالة تناقش ثغرة Insecure Deserialization
قبل الخوض في تفاصيل هذه الثغرة، نحن بحاجة لفهم مفهومين مُهمّين في لغات البرمجة:
· Object Serialization
· Object Deserialization
Object Serialization
هي عملية تحويل الـ Object إلى Bytes ، ومن ثم هذه الـ Bytes بالإمكان حفظها في ملف ( أي حفظ الحالة الخاصة بهذا الـ Object)، أو في قاعدة بيانات، أو تمريرها عبر الشبكة إلى تطبيق آخر بإستخدام أحد بروتوكولات الشبكة
Object Deserialization
هي العملية العكسية للعملية السابقة، أي لدينا بيانات مُمثلة على شكل Bytes يتم قراءتها من ملف، أو قاعدة بيانات، أو قد يتم استقبالها عبر الشبكة عن طريق أحد البروتوكولات مثل بروتوكول الـ HTTP ومن ثم يتم تحويل هذه الـ Bytes إلى صورتها الأولى الـ Object
الصورة التالية تلخص العمليتين:
هذا بالنسبة للجانب النظري، نأتي الآن للجانب التطبيقي ونأخذ المثال التالي على هذين المفهومين في لغة جافا
Object Serialization in Java
في لغة جافا إذا أردنا تطبيق هذا المفهوم وتحويل الـ Objects إلى Bytes لتُعالج في طرف آخر، فيجب علينا عمل implements للـ interface التالي :
كما يظهر في الكود التالي، تم تعريف كلاس Item والذي يقوم بعمل implement للـ Serializable ( السطر 5 )
الآن بإمكاننا تعريف أي Object من كلاس Item ومن ثم تحويل هذا الـ Object إلى Bytes stream
في الكود التالي قمنا بتعريف كلاس Serialize ، وفي دالة الـ main قمنا بتعريف Object من كلاس Item ( السطر 10 )
السطر 12 : قمنا بتعريف Object من FileOutputStream لكتابة محتوى الـ s1 ( في صورة Bytes ) في ملف يسمى data.ser
السطر 14 : في هذا السطر قمنا بنداء الدالة writeObject وتم تمرير الـ s1 لها، هذه الدالة ستقوم بتحويل الحالة الخاصة بالـ Object إلى Bytes ( هنا تحدث عملية الـ Serialization )
The Java serialized object
بعد عمل Compile للكود السابق سيكون المخرج لدينا ملف data.ser والذي يحتوي على الـ Object من كلاس Item لكن في صورة Bytes
لو قمنا بإستخدام أداة file للإطلاع على نوع ملف data.ser سنجد أن الأداة تطبع لنا الناتج الآتي :
ولو قمنا بقراءة محتوى ملف data.ser في صورة Hex باستخدام أداة hexdump سنجد أن محتوى الملف يبدأ بهذه القيمة : aced
ولو أردنا الإطلاع على المقابل لهذه القيمة في التمثيل Base64 سيظهر لنا المُخرج الآتي :
ماذا نستفيد من هذه المعلومات؟
يكفي أن نعرف إلى الآن أن الـ Java Serialized Object يحمل الـ Signature الآتي :
- في التمثيل Hex : aced
- في التمثيل Base64 : rO0AB
في المقابل، ماذا لو أردنا الإطلاع على محتوى هذا الـ Object لكن بصورة مقروءة ومفهومة لنا أكثر؟
لحُسن الحظ قام أحد الباحثين بتطوير أداة تُمكّننا من قراءة الـ Java Serialized Object بشكل أكثر وضوحًا بدلًا من قراءته بالـ Hex أو الـ Base64 ، وهذه الأداة هي : SerializationDumper
الصورة الآتية توضح إستخدام هذه الأداة والمُخرج من قراءة محتوى ملف الـ data.ser
بعد الخوض في مفهوم الـ Serialization وتحليل الناتج النهائي من هذه العملية ( Serialized Object ) لننتقل الآن للعملية العكسية المقابلة لها ، الـ Deserialization
Object Deserialization in Java
في الكود التالي قمنا بتعريف كلاس Deserialize والذي سنطبق فيه مفهوم الـ Deserialization
السطر 10 : قمنا بقراءة محتوى ملف data.ser عن طريق الـ FileInputStream و ObjectInputStream
السطر 11 : قمنا بتعريف Object من كلاس Item ومن ثم سيتم حفظ القيمة العائدة من دالة readObject في هذا الـ object
وبعبارة أخرى: دالة readObject ستقوم بقراءة محتوى ملف data.ser ( والذي هو عبارة عن Bytes ) ومن ثم هذه الـ Bytes سيتم إرجاعها إلى صورتها الأولى Object ( هنا تحدث عملية الـ Deserialization )
الآن نحن نملك المعرفة اللازمة لفهم ثغرة الـ Insecure Deserialization ولماذا تحدث؟
ننتقل لمناقشة تفاصيل هذه الثغرة في الجزء التالي من المقالة
Insecure Deserialization
بشكل مُختصر جدًا : يحدث هذا النوع من الثغرات عندما يكون بإمكان المخترق التحكّم والتلاعب بالبيانات التي يتم عمل لها Deserialize
لنفصّل أكثر ..
نقصد بالبيانات هنا الـ Object الذي تم حفظ حالته وتحويلها إلى صورة Bytes
فعلى سبيل المثال لو كان لدينا تطبيقين :
- التطبيق الأول : يقوم بتنفيذ عملية الـ Serialization على الـ Object ومن ثم يقوم بإرسال المُخرَج النهائي عبر الشبكة إلى التطبيق الثاني
- التطبيق الثاني : يستقبل هذه البيانات ومن ثم يجري عليها عملية الـ Deserialization
- المشكلة : إذا لم يقم التطبيق الثاني بالتأكد من سلامة البيانات التي يعالجها لتنفيذ عملية الـ Deserialization وكان بإمكان المخترق إعتراض هذه البيانات والتلاعب بها فهنا تحدث ثغرة الـ Insecure Deserialization !
الآن لننتقل للجزء الأخير من هذه المقالة،
التدريب على هذه الثغرة وتحليل هذا التطبيق البسيط الذي يحاكي هذه المشكلة.
java-deserialize-webapp
بدايةً نقوم بعمل Run للتطبيق كالآتي :
ومن ثم نستطيع تصفح التطبيق عبر المنفذ التالي :
127.0.0.1:8000
التطبيق سيقوم بإستقبال البيانات التي نمررها له ومن ثم سيقوم بعمل Deserialization لها بدون التأكد من سلامتها،
بإمكاننا إستغلال هذه المشكلة بطرق عدّة، لكن في هذه المقالة سنقوم ببناء إستغلال بسيط جدًا يؤكد لنا أن التطبيق مُصاب بالفعل
DNS Resolution Exploit
فكرة الإستغلال :
نقوم بتمرير Serialized Object إلى التطبيق المصاب،
وهذا الـ Serialized Object خلال عملية الـ Deserialization سيتيح لنا تنفيذ الأوامر التي نريدها،
في هذا الاستغلال سنجعل التطبيق المصاب يقوم بعمل DNS query لدومين خاص بنا
قبل بناء الـ payload التي سنقوم بإرسالها للتطبيق المصاب، سنقوم باستخدام DNS Proxy
- الغرض من هذه الخطوة :
إعتراض الـ DNS request الصادر من التطبيق المصاب، حتى نتأكد بأن الـ payload التي قمنا بإرسالها تم عمل Run لها بشكل سليم
الآن سنقوم ببناء الـ payload أو الـ Serialized Object بإستخدام هذه الأداة كالآتي :
بعد بناء الـ Serialized Object بإمكاننا الإستعانة بأداة الـ SerializationDumper التي قمنا بالإطلاع عليها سابقًا لتحليل هذا الـ Object وفهم محتواه ( هذه الخطوة للتحليل فقط، ولاحظ بأني قمت بتمرير raw data للأداة ولم أقم بتمرير Base64 )
الآن نقوم بإرسال الـ payload إلى التطبيق
بعد إرسال الـ payload نلاحظ أن التطبيق يخبرنا بأن عملية الـ Deserialization نجحت
ولو عدنا إلى الـ DNS Proxy سنجد أن التطبيق المصاب بالفعل قام بعمل DNS query
ختامًا، أود الإشارة بأن هذه المقالة تمّت كتابتها خلال دراسة هذه الثغرة والبحث حولها، فكل ما تم ذكره هنا قد يحتمل الخطأ، لكن بالإمكان العودة إلى المراجع التي إستندت عليها هذه المقالة
وإن أصبت فمن توفيق الله وحده، وإن أخطأت فمن نفسي والشيطان
مواضيع مُقترحة لفهم هذه الثغرة بتعمق أكثر: