نظرة عامة على نقاط الضعف في Mikrotik Winbox. أو ملف كبير

يوم جيد ، ربما سمع الكثير عن الضعف الأخير في أجهزة توجيه Mikrotik ، والذي يسمح لك باستخراج كلمات مرور جميع المستخدمين. في هذه المقالة ، أود أن أظهر بالتفصيل وتحليل جوهر هذه الثغرة الأمنية.
يتم توفير جميع المواد لأغراض إعلامية فقط ، لذلك لن يكون الرمز الذي يستغل الثغرة موجودًا هنا. إذا لم تكن مهتمًا بالتعرف على الأسباب والضعف الداخلي لضعف معين ، يمكنك القراءة.

لنبدأ


أول شيء للبدء هو تحليل حركة المرور بين عميل Winbox والجهاز
Winbox هو تطبيق لنظام التشغيل WIndows OS ، والذي يكرر واجهة الويب تمامًا وهو مصمم لإدارة وتكوين الجهاز مع نظام تشغيل جهاز التوجيه. يدعم وضعان التشغيل ، TCP و UDP
قبل البدء ، يجب عليك تعطيل تشفير حركة المرور في Winbox. يتم ذلك على النحو التالي: تحتاج إلى تمكين أدوات مربع الاختيار -> الوضع المتقدم . بعد ذلك ، ستتغير الواجهة على النحو التالي:


قم بإلغاء تحديد الوضع الآمن . قم بتشغيل Wireshark وحاول تسجيل الدخول إلى الجهاز:


كما ترى أدناه ، بعد التفويض ، يُطلب ملف قائمة ومن ثم يتم نقل محتوياته إلينا بالكامل ، قد يبدو أن كل شيء على ما يرام ، ولكن لنلق نظرة على بداية هذه الجلسة:


في البداية ، يرسل Winbox نفس الحزمة بالضبط يطلب ملف القائمة :


النظر في هيكلها:

  1. 37010035 - حجم العبوة
  2. M2 هو ثابت يشير إلى بداية الحزمة
  3. 0500ff01 - متغير 0xff0005 في القيمة True
  4. 0600ff09 01 - متغير 0xff0006 في القيمة 1 (عدد الرزم المرسلة)
  5. 0700ff09 07 - متغير 0xff0007 بالقيمة 7 (فتح ملف في وضع القراءة)
  6. 01000021 04 6s967374 - متغير 0x
  7. 0200ff88 02 ... 00 - مصفوفة من 0xff0002 بحجم عنصرين
  8. 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 هو لتعيين المتغير

بالإضافة إلى ذلك ، تحتوي كل حزمة على:

  1. علامات خاصة تشير إلى بداية الحزمة
  2. حجم العبوة
  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 شروط:

  1. لا يحتوي مسار الملف على " .. " ؛
  2. هناك حقوق للوصول إلى الملف ؛
  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:


مقارنة الكود الزائف


الآن الخروج من دورة التحقق يحدث عند الكشف الأول عن " .. ". صحيح أنه ليس من الواضح لي تمامًا لماذا أضافوا شيكًا لإدخال نقطة واحدة. وبسبب التغيير في آلية التنشيط الخاصة بالمستخدم المتطور ، للأسف ، لا توجد طريقة لرؤية ذلك في الديناميكيات.

لتلخيص


  1. يعالج نظام تشغيل الموجه الحزم الواردة دون مشاكل حتى قبل تفويض المستخدم
  2. بسبب عامل تصفية غير صحيح ، نحصل على أي ملف

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

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

أيضًا ، يمكن أن تكون هذه الثغرة بديلاً ممتازًا للإمكانية المعروفة سابقًا لتفعيل وضع المطور ، نظرًا لأنك لست بحاجة إلى إعادة تشغيل الجهاز ، فقم بإجراء نسخ احتياطي \ استعادة ملف التكوين.

Source: https://habr.com/ru/post/ar416067/


All Articles