
ثغرة أمنية في LINKSYS AC1900 EA7500v3 IGD UPnP تتمثل في تجاوز سعة المخزن المؤقت للمكدس مما يسمح بتنفيذ التعليمات البرمجية عن بُعد
تسمح هذه الثغرة للمهاجمين المجاورين للشبكة بتنفيذ تعليمات برمجية عشوائية على التركيبات المتأثرة من أجهزة توجيه LINKSYS EA7500. ولا يشترط المصادقة لاستغلال هذه الثغرة.
يتمثل الخلل المحدد في معالجة بيانات طلبات HTTP الموجهة إلى خدمة IGD UPnP. فعند تحليل محتويات متغير يوفره المستخدم في طلب إجراء SOAP UPnP معين، لا تتحقق العملية بشكل صحيح من طول البيانات التي يوفرها المستخدم قبل نسخها إلى مخزن مؤقت ثابت الطول في المكدس. يمكن للمهاجم استغلال هذه الثغرة لتنفيذ التعليمات البرمجية بصلاحيات الجذر (root).
المنتج/البرنامج الثابت: جميع إصدارات البرنامج الثابت لـ Linksys EA7500 بما في ذلك الإصدار Ver.3.0.1.207964 - كما تتأثر طرز أجهزة التوجيه الأخرى وإصدارات البرامج الثابتة التي تستخدم هذا الملف الثنائي
يقع الملف الثنائي لخدمة IGD UPnP في /usr/sbin/IGD وهو يعمل افتراضيًا على المنفذ 49152. توفر هذه الخدمة ملف وصف UPnP XML المسمى IGDdevicedesc.xml الذي يوضح العديد من الوظائف التي يمكن استدعاؤها باستخدام طلبات HTTP تحتوي على نص XML. إحدى هذه الوظائف هي SetDefaultConnectionService. تتطلب هذه الوظيفة متغيرًا واحدًا من نوع سلسلة نصية. عند استدعاء الوظيفة SetDefaultConnectionService وتنفيذها، لا يتحقق البرنامج من طول المتغير الذي يوفره المستخدم قبل إجراء استدعاء strncpy يتضمن هذا المخزن المؤقت. إن متغيري عنوان المصدر وحجم النسخ في استدعاء strncpy يتم التحكم فيهما من قبل المستخدم، حيث يكون حجم النسخ هو طول المتغير الذي يوفره المستخدم. ثم تُنسخ البيانات إلى مخزن مؤقت ثابت بحجم 184 بايت، مما يؤدي إلى ثغرة تجاوز سعة المخزن المؤقت للمكدس.
تُسمى الوظيفة SetDefaultConnectionService باسم _set_connection_type. تبدأ الوظيفة بتهيئة مخزن مؤقت بحجم 184 بايت ثم تحصل على مؤشر إلى المخزن المؤقت الذي يحتوي على متغير السلسلة النصية الذي يوفره المستخدم والمضمن في الطلب إذا لم يكن فارغًا (NULL). يتم ذلك عن طريق استدعاء PAL_xml_node_GetFirstbyName ثم PAL_xml_node_get_value. انظر الكود أدناه:
int _set_connection_type(int **param_1)
{
int iVar1;
char *var_value;
size_t var_value_length;
undefined uVar2;
undefined1 *puVar3;
char **ppcVar4;
undefined4 *puVar5;
char *pcVar6;
int *piVar7;
char acStack_d4 [184]; -----> /* Initializing 184-byte buffer */
memset(acStack_d4,0,0xb4);
iVar1 = PAL_xml_node_GetFirstbyName((*param_1)[0xf0],"NewConnectionType",0); -----> /* iVar1 now points to the user-supplied value */
if ((iVar1 != 0) && (var_value = (char *)PAL_xml_node_get_value(), var_value != (char *)0x0)) { -----> /* Ensures the user-supplied value is not empty and obtains a pointer to it */
...
لاحقًا في نفس الوظيفة، يتم تنفيذ استدعاء strlen للحصول على حجم السلسلة النصية التي يوفرها المستخدم بالإضافة إلى إزاحة ثابتة مقدارها 0x174. يتم تشغيل الشرط القابل للاستغلال في استدعاء strncpy اللاحق حيث تكون وسيطة الوجهة هي عنوان المخزن المؤقت الجديد بحجم 184 بايت، والمصدر هو المؤشر إلى السلسلة النصية التي يوفرها المستخدم، ويكون حجم عملية النسخ هو حجم السلسلة النصية التي يوفرها المستخدم والتي يعيدها استدعاء strlen بالإضافة إلى إزاحة ثابتة مقدارها 0x174. يؤدي ذلك إلى ثغرة تجاوز سعة المخزن المؤقت نظرًا لأن كلاً من عنوان المصدر ومتغيرات الحجم يتم التحكم فيها من قبل المستخدم ولا يتم إجراء أي عمليات تحقق من الحجم. انظر الكود أدناه:
int _set_connection_type(int **param_1)
{
...
var_value_length = strlen((char *)(iVar1 + 0x174)); ----> /* iVar1 is a pointer to the user supplied string */
strncpy(acStack_d4,(char *)(iVar1 + 0x174),var_value_length + 1); ----> /* Vulnerable strncpy call */
...
الإزاحة اللازمة للكتابة فوق عنوان إرجاع الوظيفة على المكدس هي 276 بايت. يمكن استخدام البايتات الأربعة التالية لإعادة توجيه التنفيذ إلى عنوان عشوائي وبالتالي اختطاف تدفق التحكم في البرنامج.
استخدم دالة نسخ سلاسل نصية أكثر أمانًا: بدلاً من استخدام strncpy التي لا تضمن إنهاء المخزن المؤقت الوجهة بحرف null، يمكن استخدام دالة نسخ سلاسل نصية أكثر أمانًا مثل strncpy_s أو memcpy_s. تضمن هذه الدوال أن المخزن المؤقت الوجهة ينتهي دائمًا بحرف null ولا تسمح بنسخ بيانات أكثر مما يمكن أن يحتويه المخزن المؤقت.
التحقق من صحة الإدخال: تحقق من صحة إدخال المستخدم لضمان عدم تجاوزه لحجم المخزن المؤقت الوجهة. إذا كان الإدخال أطول من حجم المخزن المؤقت، فإما أن يتم رفضه أو اقتطاعه ليتناسب مع حجم المخزن المؤقت.
استخدم مخزنًا مؤقتًا مخصصًا ديناميكيًا: خصص الذاكرة للمخزن المؤقت الوجهة ديناميكيًا بدلاً من استخدام مخزن مؤقت ثابت الحجم في المكدس. يتيح هذا الأسلوب تعديل حجم المخزن المؤقت وفقًا لحجم بيانات الإدخال. (غير موصى به للأنظمة ذات الموارد المحدودة)
تنفيذ PoC: python poc.py 192.168.1.1 49152
رابط تنزيل البرنامج: https://support.linksys.com/kb/article/559-en/ (إصدار البرنامج الثابت Ver. 3.0.1.207964)
gdbserver-7.7.1-armel-eabi5-v1-sysv المتاح هنا: https://github.com/stayliv3/gdb-static-cross/tree/master/prebuilt).pkill IGD لإنهاء الملف الثنائي ثم أعد تشغيله باستخدام ./gdbserver :1337 /usr/sbin/IGD. يمكن استخدام ملف .gdbinit التالي لبدء جلسة تصحيح أخطاء مستقرة:> set follow-fork-mode child
> set detach-on-fork off
> target remote 192.168.1.1:1337
> c