يوم جيد ، ربما سمع الكثير عن الضعف الأخير في أجهزة توجيه Mikrotik ، والذي يسمح لك باستخراج كلمات مرور جميع المستخدمين. في هذه المقالة ، أود أن أظهر بالتفصيل وتحليل جوهر هذه الثغرة الأمنية.
يتم توفير جميع المواد لأغراض إعلامية فقط ، لذلك لن يكون الرمز الذي يستغل الثغرة موجودًا هنا. إذا لم تكن مهتمًا بالتعرف على الأسباب والضعف الداخلي لضعف معين ، يمكنك القراءة.
لنبدأ
أول شيء للبدء هو تحليل حركة المرور بين عميل Winbox والجهاز
Winbox هو تطبيق لنظام التشغيل WIndows OS ، والذي يكرر واجهة الويب تمامًا وهو مصمم لإدارة وتكوين الجهاز مع نظام تشغيل جهاز التوجيه. يدعم وضعان التشغيل ، TCP و UDP
قبل البدء ، يجب عليك تعطيل تشفير حركة المرور في Winbox. يتم ذلك على النحو التالي: تحتاج إلى تمكين
أدوات مربع الاختيار ->
الوضع المتقدم . بعد ذلك ، ستتغير الواجهة على النحو التالي:
قم بإلغاء تحديد
الوضع الآمن . قم بتشغيل Wireshark وحاول تسجيل الدخول إلى الجهاز:
كما ترى أدناه ، بعد التفويض ، يُطلب ملف
قائمة ومن ثم يتم نقل محتوياته إلينا بالكامل ، قد يبدو أن كل شيء على ما يرام ، ولكن لنلق نظرة على بداية هذه الجلسة:
في البداية ، يرسل Winbox نفس الحزمة بالضبط يطلب ملف
القائمة :
النظر في هيكلها:
- 37010035 - حجم العبوة
- M2 هو ثابت يشير إلى بداية الحزمة
- 0500ff01 - متغير 0xff0005 في القيمة True
- 0600ff09 01 - متغير 0xff0006 في القيمة 1 (عدد الرزم المرسلة)
- 0700ff09 07 - متغير 0xff0007 بالقيمة 7 (فتح ملف في وضع القراءة)
- 01000021 04 6s967374 - متغير 0x
- 0200ff88 02 ... 00 - مصفوفة من 0xff0002 بحجم عنصرين
- 0100ff88 02 ... 00 - صفيف من 0xff0001 بحجم عنصرين
نتيجة لعكس البروتوكول ، والملفات الثنائية المقابلة على جانب العميل والخادم ، كان من الممكن استعادة وفهم إلى حد كبير هيكل البروتوكول الذي تتواصل من خلاله Winbox مع الجهاز.
وصف بروتوكول NvMessageأنواع الحقول (الاسم: التسمية الرقمية)
- u32: 0x08000000
- u32_array: 0x88000000
- السلسلة: 0x20000000
- string_array: 0xA0000000
- addr6: 0x18000000
- addr6_array: 0x98000000
- u64: 0x10000000
- u64_array: 0x90000000
- صحيح: 0x00000000
- خطأ: 0x01000000
- bool_array: 0x80000000
- الرسالة: 0x28000000
- message_array: 0xA8000000
- الخام: 0x30000000
- raw_array: 0xB0000000
- u8: 0x09000000
- be32_array: 0x88000000
أنواع الأخطاء (الاسم: التسمية الرقمية)
- SYS_TO: 0xFF0001
- STD_UNDOID: 0xFE0006
- STD_DESCR: 0xFE0009
- STD_FINISHED: 0xFE000B
- STD_DYNAMIC: 0xFE0007
- STD_INACTIVE: 0xFE0008
- STD_GETALLID: 0xFE0003
- STD_GETALLNO: 0xFE0004
- STD_NEXTID: 0xFE0005
- STD_ID: 0xFE0001
- STD_OBJS: 0xFE0002
- SYS_ERRNO: 0xFF0008
- SYS_POLICY: 0xFF000B
- SYS_CTRL_ARG: 0xFF000F
- SYS_RADDR6: 0xFF0013
- SYS_CTRL: 0xFF000D
- SYS_ERRSTR: 0xFF0009
- SYS_USER: 0xFF000A
- SYS_STATUS: 0xFF0004
- SYS_FROM: 0xFF0002
- SYS_TYPE: 0xFF0003
- SYS_REQID: 0xFF0006
قيم الخطأ (الاسم: التعيين الرقمي)
- خطأ: خطأ: 0xFE0006
- ERROR_TOOBIG: 0xFE000A
- ERROR_EXISTS: 0xFE0007
- خطأ غير مسموح به: 0xFE0009
- خطأ: مشغول: 0xFE000C
- خطأ: غير معروف: 0xFE0001
- ERROR_BRKPATH: 0xFE0002
- ERROR_UNKNOWNID: 0xFE0004
- ERROR_UNKNOWNNEXTID: 0xFE000B
- ERROR_TIMEOUT: 0xFE000D
- ERROR_TOOMUCH: 0xFE000E
- ERROR_NOTIMP: 0xFE0003
- خطأ: 0xFE0005
- STATUS_OK: 0x01
- STATUS_ERROR: 0x02
هيكل المجال في حزمة
في بداية أي حقل يوجد نوعه - 4 بايت (3 بايت - الغرض من المتغير ، أكثر من ذلك لاحقًا ، 1 بايت - نوع هذا المتغير مباشرة) ثم الطول هو 1-2 بايت والقيمة نفسها.
المصفوفات
المجازي ، يمكن وصف الصفيف بالبنية التالية:
struct Array { uint32 type; uint8 count; uint32 item1; uint32 item2; ... uint8 zero; }
النوع (4 بايت) / عدد العناصر (1 بايت) / العناصر (4 بايت) / في نهاية البايت \ x00
الخطوط
لا يتم إنهاء السلاسل الفارغة ، ولكن لها طول محدد بوضوح:
struct String { uint32 type; uint8 length; char text[length]; }
الأرقام
أبسط نوع في الحزمة ، يمكن تمثيله كنوع قيمة:
struct u* { uint32 type; uint8/32/64 value; }
بناءً على النوع ، تحتوي القيمة على بُعد بت مناظر.
نوع منطقي
حجم الحقل هو 4 بايت ، البايت العالي مسؤول عن القيمة (True \ False) ، البايت السفلي 3 هو لتعيين المتغير
بالإضافة إلى ذلك ، تحتوي كل حزمة على:
- علامات خاصة تشير إلى بداية الحزمة
- حجم العبوة
- أسواق مراقبة العبوات الكبيرة
الثوابت الموجودة
- 0xfe0001 - يحتوي على معرف الجلسة (بايت واحد)
- 0xff0006 - رقم الرزمة المرسلة (1 بايت)
- 0xff0007 - وضع الوصول إلى الملفات (1 بايت)
أوضاع الوصول إلى الملف
- 7 - فتح للقراءة
- 1 - مفتوح للتسجيل
- 6 - إنشاء دليل
- 4 - اقرأ
- 5- الحذف
الآن ، بمعرفة كيفية عمل البروتوكول ، يمكننا إنشاء الحزم التي نحتاجها عشوائيًا ومشاهدة كيفية استجابة الجهاز لها.
على جانب الجهاز ، يكون الملف التنفيذي
/ nova / bin / mproxy مسؤولاً عن معالجة الحزم. نظرًا لعدم حفظ أسماء الدالات ، اتصلت بوظيفة تعالج الحزمة وتتخذ قرارات حول ما يجب فعله
بملف file_handler () . ألق نظرة على الوظيفة نفسها:
ملحوظة: الكود الذي سيثير اهتمامنا يتم تمييزه بأسهم.
الخطوة الأولى
عند تلقي حزمة لفتح ملف للقراءة ، يبدأ في المعالجة من هذه الكتلة:
في البداية ، يتم استخراج اسم الملف من الحزمة باستخدام الوظيفة
nv :: message :: get <nv :: string_id> () .
بعد ذلك ، تقوم دالة
tokenize () بتقسيم السلسلة الناتجة إلى أجزاء منفصلة ، باستخدام الحرف "
/ " كمحدد.
يتم تمرير المصفوفة الناتجة إلى السلاسل إلى الدالة
path_filter () ، والتي تتحقق من المصفوفة المستلمة من السلاسل بحثًا عن وجود "
.. " ، وفي حالة حدوث أخطاء ، يُرجع خطأ
ERROR_NOTALLOWED (0xFE0009)سيتم تلقي PS ERROR_NOTALLOWED أيضًا في الاستجابة إذا لم تكن هناك أذونات للملفات
إذا كان كل شيء على ما يرام ، فإن المسار إلى دليل
الويب أو دليل
pckg متسلسل مع بداية اسم الملف
الخطوة الثانية
إذا سار كل شيء على ما يرام ، يفتح الملف ويتم حفظ مقبضه في الكائن العام.
إذا تعذر فتح الملف ، فإننا نتلقى خطأ في الرد:
لا يمكن فتح الملف المصدر .
وبالتالي ، من أجل الحصول على محتويات الملف ، يجب استيفاء 3 شروط:
- لا يحتوي مسار الملف على " .. " ؛
- هناك حقوق للوصول إلى الملف ؛
- الملف موجود ويمكن فتحه بنجاح.
الآن دعنا نحاول إرسال بعض الحزم لاختبار وظائف هذه الوظيفة:
$ ./untitled.py -t 192.168.88.1 -f /etc/passwd Error: SYS_ERRNO => ERROR_FAILED Error: SYS_ERRSTR => cannot open source file $ ./untitled.py -t 192.168.88.1 -f /../../../etc/passwd Error: SYS_ERRNO => ERROR_NOTALLOWED $ ./untitled.py -t 192.168.88.1 -f //./././././../etc/passwd Error: SYS_ERRNO => ERROR_FAILED Error: SYS_ERRSTR => cannot open source file
لذا! لكن هذا غريب بالفعل ... نتذكر أن
ERROR_NOTALLOWED يظهر إذا لم يتم اجتياز التحقق في
path_filter () ، وإلا فإننا ما زلنا نتلقى رسالة حول نقص حقوق الوصول ، ولكن في الحالة الأخيرة ، اتضح أنه تم البحث في الملف في دليل المستوى الأعلى؟
دعنا نجرب بهذه الطريقة:
$ ./untitled.py -t 192.168.88.1 -f //./.././.././../etc/passwd xvM2 1Enobody:*:99:99:nobody:/tmp:/bin/sh root::0:0:root:/home/root:/bin/sh
وعملت. لكن لماذا؟ دعنا نلقي نظرة على رمز وظيفة
path_filter () :
يظهر الكود بوضوح أن البحث عن حدوث "
.. " في الصفيف الناتج من السلاسل يحدث بالفعل. ولكن بعد ذلك الجزء الأكثر إثارة للاهتمام ، أبرزت هذا الجزء باللون الأحمر.
جوهر هذا الرمز هو أنه:
إذا كان العنصر السابق أيضًا " .. " ، فيعتبر التحقق غير ناجح. خلاف ذلك ، ضع في اعتبارك أن كل شيء على ما يرام.
على سبيل المثال لكي يعمل كل شيء ، تحتاج فقط إلى
تبديل "
/./ " و "
/../ " للتنقل بنجاح عبر أي أدلة
والانحدار إلى أي مستوى من FS.
دعونا نرى كيف أصلحها مطورو Mikrotik:
الآن الخروج من دورة التحقق يحدث عند الكشف الأول عن "
.. ". صحيح أنه ليس من الواضح لي تمامًا لماذا أضافوا شيكًا لإدخال نقطة واحدة. وبسبب التغيير في آلية التنشيط الخاصة بالمستخدم
المتطور ، للأسف ، لا توجد طريقة لرؤية ذلك في الديناميكيات.
لتلخيص
- يعالج نظام تشغيل الموجه الحزم الواردة دون مشاكل حتى قبل تفويض المستخدم
- بسبب عامل تصفية غير صحيح ، نحصل على أي ملف
بالنظر إلى الفقرات السابقة ، يمكننا بسهولة: إنشاء الملفات وحذفها وقراءتها وكتابتها ، بالإضافة إلى إنشاء أدلة عشوائية
لذلك ليس من المستغرب أن يكون الوصول إلى قراءة أي ملفات دون إذن ، أول شيء تم القيام به هو قراءة الملف بكلمات مرور المستخدم. لحسن الحظ ، تحتوي الشبكة على الكثير من المعلومات حول مكان وجودها وكيفية استخراج البيانات منها.
أيضًا ، يمكن أن تكون هذه الثغرة بديلاً ممتازًا للإمكانية المعروفة سابقًا لتفعيل وضع المطور ، نظرًا لأنك لست بحاجة إلى إعادة تشغيل الجهاز ،
فقم بإجراء
نسخ احتياطي \
استعادة ملف التكوين.