PWN Adventure 3

PWN Adventure 3

 

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

 

على الهامش :{هذه المقالة يُفترض أن تكون تكملة لسابقتها، لكن لكون الموضوع يستدعي الكثير من البحث الذي لا يتّسع له وقتي الحالي أتت هذه المقالة عوضًا عنها! }

 

 PWN Adventure 3  :

هي لعبة تم نشرها في إحدى مسابقات الـ CTF لغرض توعية مُطوري الألعاب بالثغرات المُمكن حدوثها نتيجة لبعض الأخطاء التي قد يقوم بها هؤلاء المبرمجين، اللعبة أيضًا موجّهه للمُهتمين بتحليل الـ  Binaries ، كما أنها ليست محصورة في هذا المجال فقط! توجد ثغرات في مجالات مختلفة أيضًا ( إذا أردت الاستزادة اطلع على هذه الصفحة).

 

في هذه المقالة نحن بصدد :

أولًا : تحليل بسيط جدًا للعبة

ثانيًا : إلقاء "القليل" من الضوء على بروتوكول الشبكة المُستخدم في اللعبة،

أخيرًا : سنقوم ببناء Proxy Server يكون حَلقة الوصل بين الـ Client ( برنامج اللعبة الأساسي ) و الـخادم الخاص باللعبة Game Server

 

لكن قبل البدء أود الإشارة بأن كل ما سيتم شرحه لاحقًا تم على بيئة إفتراضية ( Local Machines ) ولم يتم تطبيقه على الخادم الحقيقي الخاص باللعبة.

 

إذا أردت تثبيت اللعبة على بيئتك الإفتراضية اتبع الخطوات التي تم شرحها في هذه الصفحة

 

بسم الله لنبدأ

 

تحليل اللعبة

بعد الإنتهاء من تثبيت اللعبة سنجد البرنامج الخاص باللعبة في هذا المسار ( في حال لينكس ) :

PwnAdventure3/PwnAdventure3/Binaries/Linux

 

 

قبل تشغيل اللعبة لنقوم بجمع بعض المعلومات عن ملف اللعبة نفسه عن طريق الأداة : file

A close up of a sign

Description automatically generated

 

نلاحظ هنا أن الملف التنفيذي ELF يحمل الخاصيّة stripped ، أي أنه ليس بإمكاننا إستخراج بعض المعلومات عن هذا الـ binary  من خلال عملية الـ Debugging بالتالي قد تكون عملية التحليل هنا صعبة بعض الشيء!

 

لكن ماذا عن المكتبات أو ما يسمى بالـ Dynamic Libraries  التي تستخدمها اللعبة؟

نستطيع معرفة المكتبات المستخدمة في اللعبة عن طريق التعليمة :  ldd

A picture containing building

Description automatically generated

 

هذه قائمة بالمكتبات التي تقوم اللعبة بإستخدامها فور تشغيلها، لعل أهمها هنا هذا الملف :

libGameLogic.so

 

لنقوم بإستخدام نفس تعليمة الـ file السابقة حتى نتعرّف على هذا الملف أكثر!

A close up of a sign

Description automatically generated

 

على عكس ملف اللعبة الأساسي، ملف المكتبة لا يملك الخاصية stripped أي بإمكاننا إستخراج بعض المعلومات عن اللعبة من خلال هذا الملف .. الآن أترك لك متعة الإبحار والإستكشاف هُنا :[   

 

 

حتى الآن قمنا بجمع بعض المعلومات بدون تشغيل اللعبة، لنبدأ بتشغيل اللعبة من لوحة الأوامر كالآتي :

 

ومن ثم ننظر قليلًا إلى العملية Process الخاصة باللعبة عن طريقة التعليمة :  pstree -aslp

A screen shot of a computer

Description automatically generated

 

ماذا عن المساحة التي تقوم بإستخدامها اللعبة في الذاكرة وقت تشغيلها ؟ كيف تبدو ؟ وماذا يُكتب فيها ؟

نستطيع معرفة مثل هذه المعلومات عن طريق الدخول لمجلد /proc ومن ثم الدخول للمجلد الخاص بالـ process id ، هنا رقم العملية 3893

أي ستكون التعليمة كالآتي :

cd /proc/[pid]

 

 ومن ثم سنجد في داخل هذا المجلد العديد من المجلدات والملفات التي تحمل معلومات عن العملية الخاصة باللعبة، حتى نستطيع معرفة كيف تبدو الذاكرة نقوم بقراءة الملف maps كالآتي :

cat /proc/[pid]/maps

 

يحتوي هذا الملف على العديد من المعلومات التي لسنا بصدد تحليلها هنا لكن لعل أهمها هو عناوين الذاكرة و الصلاحيات ، على سبيل المثال هذا عنوان الذاكرة للـ Stack

 

قبل البدء في الجزء الثاني من المقالة، أود الإشارة بأنه توجد العديد من الأدوات التي تسهّل عملية التحليل وقراءة المخرجات بطريقة أكثر سهولة من ما تم ذكره سابقًا، مثلًا هذه الأداة ، لم أقم بإستخدامها حقيقة لكن أعتقد أنها جيدة، فالملخّص لا تعتقد أن الأمر جدًا مُعقّد ومستحيل!، الأمر فقط يحتاج القليل من الصّبر والعديد من أكواب القهوة :[

 

 

نظرة على بروتوكول الشبكة المُستخدم في اللعبة

 خلال تحليل البيانات التي تعبر بين الـ Client والـ Game Server عبر الشبكة قمت بإستخدام هذه الإضافة في برنامج الـ Wireshark حتى تسهّل قراءة الـ Packet ومحتوياتها

 

الفكرة من هذه الإضافة  هو أن مطوّرها قام بتحليل بُنية الـ  Packet المُتبادلة بين الـ Client والـ Game Server وكتب هذا الـ script  الذي يُترجم البيانات المضمّنة في داخل الـ Packet  إلى معنى مقرُوء و مفهوم بدلًا من أن تظهر في برنامج الـ Wireshark كـ Hex  أو ASCII

على سبيل المثال هذه إحدى الـ Packet  بدون إستخدام الإضافة :

 

وهذه بعد إضافة الـ script :

 

A screenshot of a cell phone

Description automatically generated

 

* على الهامش: { الألوان في الصورتين ليس لها علاقة بالإضافة، فقط قمت بعمل Screenshot من أجهزة مختلفة : [ }

بعد الإستعانة بالأداة السابقة وقراءة بعض المقالات في هذه السلسلة

تعرفت على أن كل Packet  يقوم بإرسالها الـ Client تحمل ID ، وهذا الـ ID يتم تخزينه في أول 2 Byte ، الهدف من هذا الـ ID هو أن يتعرّف الـ Game Server على الـ Packet التي إستقبلها بالتالي يستطيع معالجتها والرد عليها بطريقة مُناسبة .. كما أن هذا الـ ID يُعرِّف الـ Server بالمحتويات او البيانات التي تحملها هذه الـ Packet بداخلها، لن أطيل الشرح هنا سنلقي نظره على هذه التفاصيل في الجزء الثالث من هذه المقالة

 

لننتقل الآن لبناء الـ Proxy Server

 

بناء Proxy Server :

في الوضع الطبيعي ستكون عملية التواصل بين الـ Client والـ Game Server كما هو مُمثّل بالرسم الآتي:

A close up of a device

Description automatically generated

 

 

لأجل إتمام عملية تسجيل الدخول او الـ authenticate مع خادم اللعبة سيتم إستخدام المنفذ : 3333

ولبدء اللعبة سيتم إستخدام أحد هذه المنافذ 3000  إلى 3010

 

بعد بناء الـ Proxy Server ستكون عملية الإتصال بين الـ Client والـ Game Server  كالآتي :

A close up of a logo

Description automatically generated

 

 

حتى نستطيع تطبيق السيناريو في الرسم أعلاه نحتاج:

1 - أن يكون الـ Proxy  للـ Game Client  بمثابة الـ Game Server               

2 - أن يكون الـ Proxy للـ Game Server بمثابة الـ Game Client

3 - أن يعمل هذا الـ Proxy  على جميع المنافذ المستخدمه في اللعبة

 

لماذا الخطوة رقم 1 و 2؟

بما أن الـ Proxy سيكون في المنتصف بين الـ Client والـ Game Server فيجب أن يؤدي دور كِلا الطرفين في كل حالة، فإذا أردنا التخاطب مع الـ Client يجب أن يقوم الـ Proxy الخاص بنا

بأداء دور الـ Game Server لهذا الـ Client

ونفس الشيء في الطرف المقابل، لو أردنا التخاطب مع الـ Game Server سيكون الـ Proxy  الخاص بنا هنا عبارة عن Client لهذا الخادم

 

سنرى الآن كيف نقوم ببناء كل خطوة

 

1 - أن يكون الـ Proxy  للـ Game Client  بمثابة الـ Game Server               

قمت بإستخدام لغة جافا ببناء هذا الـ Proxy  

لماذا جافا تحديدًا ؟

لا يوجد سبب بالإمكان استخدام أي لغة تتقنها بشكل كافي، وفي حالتي كانت الجافا الخيار الأمثل.

 

حتى أحقق هذه الخطوة استخدمت مكتبة SocketServer

وهذه المتغيرات التي تم تعريفها في الكود لكلا الطرفين : Proxy و Game Client

 

A screen shot of a computer

Description automatically generated

 

بالنسبة للمتغيرين الآتيين :

-       fromClient   : هو متغير يحمل قيم البيانات ( Packets ) التي يقوم برنامج اللعبة بإرسالها للخادم ( في حالتنا هنا الخادم هو الـ Proxy  )

-       toClient  :  هذا المتغيّر يحمل البيانات ( Packets ) التي استقبلها الـ Proxy من الـ Game Server  ومن ثم سيقوم بتمريرها للـ  Game Client  كما سنرى في الأكواد القادمة.

 

وهذا الجزء في الكود يُمثّل عملية التواصل بين الـ  Proxy  والـ Game Client

A screenshot of a cell phone

Description automatically generated

 

2 - أن يكون الـ Proxy للـ Game Server بمثابة الـ Game Client

حتى أحقق هذه الخطوة استخدمت مكتبة Socket

هذه المتغيرات الخاصة بالـ  Game Server  في الكود :

بالنسبة للمتغيرين الآتيين :

-       fromGameServer   : هو متغير يحمل قيم البيانات ( Packets ) التي يقوم خادم اللعبة بإرسالها للـ Proxy  ، ومن ثم سيقوم الـ  Proxy بإعادة توجيهها للـ Game Client

-       toGameServer  :  هي البيانات التي قام الـ Proxy  بقراءتها من الـ Game Client  ويقوم بإعادة إرسالها للخادم الأساسي للعبة عبر هذا المتغيّر .

 

هذا الجزء من الكود يُمثّل عملية التواصل بين الـ Proxy  والـ Game Server

A screenshot of a cell phone

Description automatically generated

 

 لو حاولنا الجمع بين المتغيرات في الكود و السيناريو السابق ، سيبدو الرسم كالآتي: 

 

A screenshot of a cell phone

Description automatically generated

 

 

 

 

 3 - أن يعمل هذا الـ Proxy  على جميع المنافذ المستخدمه في اللعبة

لتحقيق هذه الخطوة قمت بتشغيل البرنامج ( الـ Proxy ) من لوحة الأوامر مع تمرير المنفذ الذي سيعمل عليه ، أي بإختصار قمت بتشغيل البرنامج عدة مرّات وكل مرّة أمرر منفذ مختلف، بالطبع هذه الطريقة ليست جيدة ويوجد طُرق أخرى لتحقيق هذه المهمّة ، لكن لضيق الوقت "كالعادة :[" لجئت لهذا الحل السريع.

 

بعد أن انتهينا من بناء الـ Proxy ، لنبدأ بمقاطعة سير البيانات بين الطرفين

لكن قبل هذا يجب عليك عمل خطوتين مهمتين جدًا

 

أولًا : التعديل على ملف الـ /etc/hosts ( في حالة لينكس ) كالآتي :

<ProxyIP>     <GameServerHostname>

الغرض من هذه الخطوة هو إيهام الـ Game Client  بأن الـ Proxy هو خادم اللعبة

 

ثانيًا: إضافة الـ Hostname الخاص بالـ GameServer في الكود في المتغير الآتي :

A close up of a logo

Description automatically generated

الآن لنبدأ بتشغيل هذا الـ Proxy ونرى كيف سيتم تبادل البيانات بين الطرفين !

بداية ، البرنامج يعمل بهذه الطريقة :

java Proxy <portNumber>

 

ولأن الـ Game Client  في البداية سيقوم بعملية تسجيل الدخول ، قمت أولًا بتشغيل البرنامج على المنفذ : 3333  

A picture containing object

Description automatically generated

بعد ذلك نبدأ بتشغيل اللعبة ومن ثم سنلاحظ أن الـ Proxy استقبل الـ Packet  التي قام الـ Client بإرسالها

A black sign with white text

Description automatically generated

 

في الطرف المقابل أيضًا نرى أن الـ Game Server  قام بإرسال بعض البيانات للـ Proxy ( إتمام عملية تسجيل الدخول )

A black sign with white text

Description automatically generated

 

بعد إكتمال عملية تسجيل الدخول ، يجب أن نبدأ تشغيل الـ Proxy على بقية المنافذ حتى نستطيع بدء اللعب!

A white sign with black text

Description automatically generated

 

الآن لننتقل لمرحلة مختلفة وهي تحليل بسيط جدًا للـ Packet التي يتم تبادلها بين برنامج اللعبة والخادم!

 

عملية تبادل الـبيانات بين الـ Client والـ Server  يجب أن تتم بطريقة مفهومة لكلا الطرفين

وفي حالة اللعبة هذه، قام مطوروها ببناء بروتوكول التواصل بين كلا الطرفين بحيث يكون لكل Packet بُنية (Structure ) معينة ورقم خاص (ID ) بهذه الـ Packet

 

على سبيل المثال :

عندما يقوم اللاعب في اللعبة بالتحرك في الخريطة الخاصة باللعبة يقوم البرنامج الخاص باللعبة بإرسال هذه المعلومات ( إحداثيات اللاعب في الخريطة ) إلى خادم اللعبة ، وهذه البيانات يتم إرسالها في Packet معينة تملك ID  خاص بها

 

أيضًا لو قام اللاعب بالقفز سيتم إرسال Packet أخرى تحمل ID مختلف تخبر خادم اللعبة بأن اللاعب قام بالقفز

 

وكل ما يحدث في طرف الـ Client يجب أن يتم إعلام خادم اللعبة به بطريقة معينة متفق عليها بين الطرفين ( لو أردت معرفة المزيد أرشح لك الإطلاع على هذه السلسلة)

 

الآن لنختم هذه المقالة بإلقاء نظرة على إحدى هذه الـ Packets

 

Location Packet

هذه الـ Packet  تحمل الـ ID الآتي : 6d76

 

قمت بإضافة دالة بسيطة للـ Proxy تتعرّف على هذه الـ Packet  في حال قام الـ Client بإرسالها

A screenshot of a cell phone

Description automatically generated

الدالة تقوم بمقارنة أول 2 Byte  في الـ Array التي تحمل البيانات التي قام الـ Client بإرسالها ، وتعيد القيمة true في حال كانت القيم مساوية للـ ID الخاص بهذه الـ Packet

 

نستطيع أن نرى النتائج في لوحة الأوامر كالآتي :

Jump Packet

 نفس الفكرة السابقة تم تطبيقها هنا مع إختلاف الـ ID الخاص بهذه الـ Packet ، فالـ ID الخاص بها هو : 6a70

 

وهذه هي الدالة التي تقوم بالتعرّف على هذه الـ Packet

A screenshot of a cell phone

Description automatically generated

 

وفي لوحة الأوامر نرى هذه النتائج :

A close up of a logo

Description automatically generated

 

قبل أن أختم أود ذكر بعض الملاحظات التي رُبما قد تكون مُفيدة لمن أراد الخوض في تحليل بروتوكول الشبكة الخاص بهذه اللعبة:

·      جرّب أن تقوم بعمل Packet Injection  :

-       قمت بكتابة بعض الدوال هنا التي تقوم بهذه المهمة، بعضها هذه الدوال يقوم فعلًا بتعديل محتوى الـ Packet لكن في المقابل لا يوجد تغيير يحدث بعد إرسال هذه الـ Packet للخادم ، على سبيل المثال : في الـ Location Packet إستطعت الكتابة فوق قيم الإحداثيات ( x,y,z ) لكن مكان اللاعب لا يتغيّر ، أتصوّر أنه يوجد في طرف الخادم آلية تقوم بالتحقّق بأن الإحداثيات التي يقوم اللاعب بإرسالها يجب أن تكون في نطاق معيّن بحسب المكان الذي يتواجد به اللاعب؟ ( لأن اللعبة عبارة عن خرائط) هذه مجرّد فرضية لم أقم بإختبارها فلا تستند عليها بشكل كامل!

-       إذا أردت التأكد بأنك قمت بالفعل بالكتابة فوق القيم التي تحملها الـ Packet  إستخدم برنامج الـ Wireshark وراقب القيم هناك

-       خذ بعين الإعتبار بأن البيانات التي تعبر عبر الشبكة قد تكون معكوسة، وأقصد هنا هذه المفاهيم:  Network Byte order  والـ Host Byte order  والـ Little-endian  والـ Big-endian  ، فإذا أردت حقن قيم في الـ Packet يجب أن تعرف بأي ترتيب ستقوم بحقن هذه الـ Bytes

 

ختامًا ، إن أصبت فمن توفيق الله وحده ..

وإن أخطأت فمن نفسي والشيطان.

 

 

المراجع :

-       https://github.com/LiveOverflow/PwnAdventure3

-       https://github.com/Foxmole/PwnAdventure3

-       https://github.com/beaujeant/PwnAdventure3

الكود المصدري الخاص بالـ Proxy :

-       https://github.com/0xb1tByte/PWN/tree/master/PwnAdventure