في المقال الماضي كنت تحدثت عن طريقة لعمل Local Privilege Escalation من خلال الـMysql وكما أخبرتكم في المقال السابق أنني بعد حصولي على حساب مستخدم محدود على النظام وجدت طريقتين لعمل Privilege Escalation أو لزيادة صلاحياتي. الأولى وهي من خلال ثغرة Buffer Overflow داخل ملف مملوك للروت والثانية هي التي قمت بشرحها في المقال السابق والتي كانت تستغل ثغرة موجودة في الـMysql.

التحدي كان كالتالي، بعد أن قمت بالحصول على الحساب ذو الصلاحيات المحدودة قمت بالتنقل في بعض المسارات ووجدت مجلد اسمه SECRET وكان بداخله ثلاثة مجلدات door1 وdoor2 وdoor3، قمت باستخدام أمر ls لرؤية محتويات المجلدات بهذه الطريقة:

ls

ما نلاحظه في الصورة السابقة هو حجم الملفات، يوجد ملفين متطابقين في الحجم والملف الموجود في door1 هو الملف الوحيد المختلف في الحجم.

سأقوم بأخذ نسخه من الملف للمسار /tmp لأنه من الواضح أنه يوجد سكربت يقوم بنقل مكان الملف المختلف ما بين المجلدات الثلاثة. ولكن لاحظ أنني سأقوم ببدأ العمل عليه للوصول إلى الـShellCode النهائي ولكن يجب أن تقوم بتنفيذ الـShellCode النهائي على الملف الأصلي وليس الملف الموجود في /tmp وذلك لأنك لو لاحظ فالملف الذي قمت بنسخه ستجد أنه مملوك للمستخدم ذو الصلاحيات المحدودة وليس الروت كما هو الحال مع الملف الأصلي.

قبل أن أبدأ العمل على الملف سأقوم بتحميل إضافة للـGDB ستساعدني كثيرًا في العمل من خلال الـGDB وهي إضافة يمكنك تحميلها من الرابط التالي واسمها Peda-GDB:

https://github.com/longld/peda

بعد تحميل الملف المضغوط يمكنك نقله إلى الهدف من خلال الـnc أو إن كان الهدف متصل بالإنترنت يمكنك تحميلها مباشرة على الهدف، قم بفك الضغط على الملف المضغوط وبعد ذلك قم بإضافة الـpeda.py لملف إعدادات الـGDB كما هو موضح في الصورة التالية:

add peda script to gdb config file

لاحظ أن سكربت الـpeda.py موجود داخل الـ/tmp/peda/ فإن كان السكربت موجود في مسار أخر قم بكتابة المسار الصحيح في الأمر السابق.

بعد ذلك يمكننا أن نقوم بفتح الملف الذي سنقوم باختباره داخل الـgdb ولكن قبل ذلك أريد أن أتأكد إن كانت الـASLR مفعلة على النظام وذلك من خلال الأمر التالي:

checking for aslr

بالتأكد من القيمة الموجودة داخل ملف randomize_va_space وجدت أن القيمة الموجودة داخله هي 2 وهذا يعني أن الـASLR مفعلة بكامل قوتها 🙁

دعنا نتأكد من خلال أمر ldd فهو يظهر لي الـShared Libraries الخاصة بالملف ومكانها في الميموري، إن كان يتغير فهذا معناه أن الـASLR بالفعل مفعلة:

ldd file changing addresses

لاحظ أنها العناوين في الميموري تتغير مع كل مرة أقوم فيها بتشغيل أمر ldd وهذا معناه إن الـASLR مفعل.

ماذا أفعل لحل هذه المشكلة؟

checking for kernel build

يبدو أن النظام من نوع 32Bit، هذا جيد.. يوجد أكثر من طريقة لتخطي الـASLR ولكن أسهلها بما أن النظام من نوع 32Bit فهي استخدام أمر ulimit 🙂 فالأمر يتحكم في مصادر النظام التي يتم إعطائها للمستخدم، دعنا نرى ماذا سيحدث لو قمنا باستخدامه لإعطاء الـStack قيمة unlimited 🙂

ulimit ldd static addresses

كما نلاحظ فالعناوين أصبحت ثابتة =D

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

checking for crash

كما نرى في الصورة السابقة قمت بتشغيل الملف وكـArgument قمت باستخدام لغة البايثون لتمرير 500 حرف A وحدث كما نرى، Crash 🙂 هذا معناه أننا من الممكن أن نقوم بمحاولة استغلال هذا الخطأ البرمجي كثغرة.

دعنا الآن ننتقل للـGDB، وكما سنلاحظ سنجد أن الـPeda يعمل 🙂

gdb

الخطوة الأولى في استغلال ثغرات الـBuffer Overflow هي تحديد مكان الـCrash بالضبط، بمعنى أنني يجب أرسلت 500 حرف ولكن عند أي حرف بالضبط حدث الـCrash وهذا ما أريد تحديده، يمكننا فعل ذلك ببساطة من خلال إرسال Unique Characters وذلك لتحديد مكان الـCrash بالضبط كما سنرى في الخطوات التالية. يمكننا استخدام سكربت Peda لفعل ذلك ولكنني سأشرح كيفية فعل ذلك بدونها. سنقوم باستخدام سكربتين داخل مشروع الميتاسبلويت الأول سنستخدمه لعمل الـPattern أو الـUnique Characters والثاني سنستخدمه لتحديد مكان الـCrash. دعنا نرى كيف سنقوم بذلك الآن:

pattern create

ما قمت به في الصورة السابقة هو أنني قمت بالبحث عن السكربت وبعدها قمت باستخدامه لصنع Unqiue Characters عددها 500، الآن سأقوم بنسخها وإرسالها للملف من خلال الـGDB كما هو موضح في الصورة التالية:

sending pattern

حرف الـr الموجود في البداية استخدمته لإخبار الـGDB أنني أريد عمل Run للملف.

النتيجة ستكون كالتالي:

crash address

هذا هو العنوان الذي حدث عنده الـCrash، دعنا الآن ننتقل للكالي مجددًا ونستخدم سكربت أخر لتحديد مكان العنوان بالضبط:

pattern offset

السكربت قام بتحديد مكان الـCrash بالضبط وهو عند 171 Character 🙂 رائع، هذا يعني أنني أستطيع التأكد من سيطرتي على الـEIP من خلال إرسال 4 Characters بعد الـ171 Character للتأكد من أنني استطعت الوصول لعنوان الـEIP الصحيح:

controling eip

رائع، بالفعل الـ4 Characters التي قمت بإرسالها هي التي ظهرت في الـEIP. للذين سيسألون عن هوية الـ”\x90″ التي قمت بإرسالها في أخر الأمر فلمن لا يعرفها هذه قيمة الـNo Operation في الـHex وهي تخبر البرنامج بأن أريد تنفيذ No Operation وعدد مراتها 16 ونقوم باستخدامها عادةً في ثغرات الـBuffer لكي يعمل الـShellCode الخاص بنا بدون مشاكل.

الآن بعد أن قمت بتحديد مكان الـCrash وقمت بالتأكد من أنني أسيطر على الـFlow الخاص بالبرنامج أو التحكم في توجيه البرنامج. نرى أن الـESP مكان جيد لتنفيذ الأوامر الخاصة بي والتي أريد من البرنامج أن يقوم بها. لكن كيف أقوم بتوجيه تحكم البرنامج للـESP؟ يوجد تعليمة أو Instruction تسمى jmp esp أو call esp وكلاهما يقومان بنفس المهمة تقريبًا وهي توجيه البرنامج للـESP. لكي أستطيع توجيه تدفق تشغيل البرنامج للـESP يجب أن أبحث داخل البرنامج عن هذه التعليمة.. سأقوم باستخدام سكربت peda ليقوم بهذه المهمة ببساطة كما سنرى في الصورة التالية:

searching for jmpcall esp

الأمر السابق معناه أنني أريد أن أبحث عن أي تعليمة داخل البرنامج تحتوي على jmp esp أو call esp ولكن النتيجة سلبية للأسف 🙁

ماذا أفعل الآن؟ هل فشلت الآن؟

لا، يمكننا أن نبحث داخل الـShared Library الخاصة بالبرنامج وذلك كما بالصورة:

searchng for jmpcall esp in shared libraries

رائع، يوجد الكثير من العناوين التي تحتوي على هذه التعليمة.

سأقوم باختيار العنوان الأول، لاحظ أنه عليك اختيار عنوان لا يحتوي على أي Bad Characters. والـBad Characters المشتركة بين معظم البرامج هي “\x00\x0a\x0d”.. تجنب أي عناوين تحتوي على أيًا من هذه القيم الثلاثة.. يمكن أن تزيد عدد الـBad Character على حسب البرنامج الذي تتعامل معه ولكن هذه القيم الثلاثة هي المشتركة بين معظم البرامج.

سأقوم أولاً بتحديد الـRET الخاص بالـMain داخل البرنامج وسأقوم بعمل Breakpoint عندها وذلك لأرى كيف سيعمل البرنامج بعد أن أقوم بمحاولة توجيهه:

disassembly main

أمر disass تعني أنني أريد عمل DisAssembly للـMain داخل البرنامج، بعدها سأقوم بعمل Breakpoint عند عنوان الـRET كما أخبرتكم:

breakpiont ret

دعنا الآن نعيد تشغيل البرنامج ونرسل الكود الجديد 🙂

الكود سيكون كالتالي، سنرسل 171 حرف A، وبدلاً من الـ4 Character سنقوم بوضع عنوان الـJMP ESP التي قمنا باختيارها ولكن لاحظ أنك يجب أن تقوم بإضافتها بطريقة Little-Endian وذلك لأننا نستهدف Intel 32 وهي تعني أنك يجب أن تقوم بإضافة العنوان بالعكس (سترى كيف في الصورة القادمة)، وبعد ذلك سنرسل الـNop Slide الخاصة بنا 🙂

adding jmp esp address

لاحظ طريقة كتابة العنوان، هذه هي طريقة الـLittle-Endian. دعنا الآن نرى تأثيره على البرنامج:

checking the program flow

لاحظ أن البرنامج توقف عند الـBreakpoint التي قمنا بصنعها وهي الـRET. دعنا نحاول الانتقال للـInstruction التالية في البرنامج لنرى ماذا يحدث بعد الـRET:

next instruction

لاحظ أنني عندما حاولت الانتقال إلى الـNext Instruction أخبرني أن هناك خطأ في عنوان الميموري. هذا رائع.. فهذا مؤشر جيد 🙂

دعنا نتأكد من القيم الموجودة داخل الـEIP والـESP، الـEIP يجب أن تحتوي على عنوان الـJMP ESP الخاص بنا، والـESP يجب أن تحتوي على أي شيء أرسلته للبرنامج بعد عنوان الـJMP ESP وفي هذه الحالة سيكون الـNop Sled 🙂

information about eip esp register

رائع، الـEIP بالفعل يحتوي على عنوان الـJMP ESP الذي وضعناه 🙂 الأمر السابق معناه (i)nfo (r)egister وهي تظهر لي معلومات عن الـRegister وفي هذه الحالة معلومات عن الـRegister الـEIP والـESP.

الخطوة التالية هي التأكد من محتوى الـESP وسنقوم بذلك كما هو موضح في الصورة التالية:

Examine the value inside esp

رائع، الـESP يحتوي بالفعل على الـNop Sled الخاصه بنا، أمر x/x يعني أظهر لي القيمة الموجودة داخل Register.

حسنٌ، بما أنني تأكد من أن كل شيء يعمل بنجاح وأنني أسيطر على الـEIP وتدفق البرنامج، أستطيع الآن إضافة الـShellCode الخاص بي ونرى النتيجة. يمكنك من خلال الـPeda توليد ShellCode صغير الحجم كما سنرى في الصورة التالية:

shellcode generate

رائع، دعنا الآن نقوم بإضافته للكود الخاص بنا وبعدها نرى تأثيره على البرنامج 🙂

final shellcode

هذا هو الكود النهائي، كل ما علينا فعله الآن هو محاولة تشغيله على البرنامج نفسه، في هذه الحالة لن أقوم بتشغيله على الملف الموجود في مجلد /tmp ولكن سأقوم بتشغيله على الملف الأصلي ولكن بعد التأكد من المكان فربما تغير مكانه الآن 🙁

door3 file

يبدو انه انتقل بالفعل للـdoor3، دعنا الآن ندخل على مجلد door3 ونجرب الـShellCode الخاص بنا ونرى النتيجة، ما نريد أن نصل إليه هو Shell بصلاحيات root:

Executing Final Shellcode

كما نرى فقد تم إضافة صلاحيات الروت للـShell الجديد 🙂 رائع جدًا، نجحنا في زيادة صلاحيات المستخدم ذو الصلاحيات المحدودة من خلال استغلال ثغرة Buffer Overflow..

ملحوظة :  لو كانت الـASLR مفعلة لن ينجح الـShellCode النهائي، تأكد من أنك قمت بتخطيها.

بهذا نكون انتهينا من هذا المقال وأتمنى أن تكونوا قد استفدتم وإن شاء الله نلتقي في مقالات أخرى شيقة وممتعة 🙂