
CVE-2018-4248: قراءة خارج النطاق في libxpc أثناء تسلسل السلسلة النصية.
xpc-string-leak هو استغلال إثبات مفهوم لقراءة ذاكرة خارج الحدود في libxpc. يستخدم هذا الاستغلال الثغرة لقراءة ذاكرة كومة خارج الحدود من diagnosticd، وهي عملية جذرية غير محمية بصندوق رمل تملك صلاحية task_for_pid-allow.
في macOS 10.13.5 و iOS 11.4، لا تتحقق الدالة _xpc_string_deserialize من أن السلسلة المُعاد بناءها بالطول الصحيح قبل إنشاء كائن سلسلة XPC عبر _xpc_string_create. يمكن أن يؤدي هذا إلى قراءة خارج الحدود بأسلوب heartbleed من الكومة إذا تم تسلسل سلسلة XPC في رسالة XPC أخرى.
فيما يلي تنفيذ _xpc_string_deserialize، بعد فك التجميع باستخدام IDA:
OS_xpc_string *__fastcall _xpc_string_deserialize(OS_xpc_serializer *xserializer)
{
OS_xpc_string *xstring; // rbx@1
char *string; // rax@4
char *contents; // [rsp+8h] [rbp-18h]@1
size_t size; // [rsp+10h] [rbp-10h]@1 MAPDST
xstring = 0LL;
contents = 0LL;
size = 0LL;
if ( _xpc_string_get_wire_value(xserializer, (const char **)&contents, &size) )
{
if ( contents[size - 1] || (string = _xpc_try_strdup(contents)) == 0LL )
{
xstring = 0LL;
}
else
{
xstring = _xpc_string_create(string, size - 1);
LOBYTE(xstring->flags) |= 1u;
}
}
return xstring;
}
تستدعي _xpc_string_deserialize أولاً _xpc_string_get_wire_value للحصول على مؤشر إلى بيانات السلسلة وكذلك الحجم المتسلسل للسلسلة، كما هو موضَّح في ترويسة السلسلة. ثم تتحقق _xpc_string_deserialize من وجود مُنهي فارغ في نهاية الحجم المُبلغ عنه، لكنها لا تتحقق بشكل حاسم من عدم وجود مُنهي فارغ في وقت سابق داخل البيانات. أخيرًا، تنشئ نسخة من السلسلة في الكومة وتنشئ كائن OS_xpc_string باستخدام _xpc_string_create.
فيما يلي الكود المفكوك لـ _xpc_string_create:
OS_xpc_string *__fastcall _xpc_string_create(const char *string, size_t length)
{
OS_xpc_string *xstring; // rax@1
xstring = (OS_xpc_string *)_xpc_base_create(&OBJC_CLASS___OS_xpc_string, 16LL);
if ( (((_DWORD)length + 4) & 0xFFFFFFFC) + 4 < length )
_xpc_api_misuse("Unreasonably large string");
xstring->wire_length = ((length + 4) & 0xFFFFFFFC) + 4;
xstring->string = string;
xstring->length = length;
return xstring;
}
تثق _xpc_string_create بقيمة length المُمرَّرة من _xpc_string_deserialize وتعين الحقول المناسبة في كائن OS_xpc_string. عند هذه النقطة، قد تحتوي السلسلة المُعاد بناءها على حقل length أكبر من بيانات السلسلة المخصصة.
نظريًا، يمكن استخدام هذا لتحفيز تلف الذاكرة في الخدمات التي تحصل على طول السلسلة باستخدام xpc_string_get_length، لكن يبدو هذا النمط غير شائع. الاستراتيجية الأقل قوة لكن الأكثر عملية هي جعل السلسلة يُعاد تسلسلها وتُرسل إلينا مرة أخرى، مما يمنحنا نافذة بأسلوب heartbleed على ذاكرة العملية الضحية.
هذا هو تنفيذ _xpc_string_serialize:
void __fastcall _xpc_string_serialize(OS_xpc_string *string, OS_xpc_serializer *serializer)
{
int type; // [rsp+8h] [rbp-18h]@1
int size; // [rsp+Ch] [rbp-14h]@1
type = *((_DWORD *)&OBJC_CLASS___OS_xpc_string + 10);
_xpc_serializer_append(serializer, &type, 4uLL, 1, 0, 0);
size = LODWORD(string->length) + 1;
_xpc_serializer_append(serializer, &size, 4uLL, 1, 0, 0);
_xpc_serializer_append(serializer, string->string, string->length + 1, 1, 0, 0);
}
يُثق بمعامل length في OS_xpc_string أثناء التسلسل، مما يعني أن عدة بايتات تُقرأ من الكومة إلى الرسالة المتسلسلة. إذا كانت السلسلة المُعاد بناءها أقصر من طولها المُبلغ عنه، فستمتلئ الرسالة ببيانات كومة خارج الحدود.
ما زلنا مقيدين باستغلال خدمات XPC التي تعكس جزءًا من رسالة XPC إلى العميل، لكن هذا أكثر شيوعًا. على سبيل المثال، في macOS و iOS، تُعد diagnosticd مرشحًا واعدًا، وهي أيضًا غير محمية بصندوق رمل وجذرية وتملك صلاحيات task_for_pid. diagnosticd مسؤولة عن معالجة الرسائل التشخيصية (مثل الرسائل المولَّدة بواسطة os_log) وبثها إلى العملاء المهتمين باستلام هذه الرسائل. من خلال التسجيل لاستلام تدفقنا التشخيصي الخاص ثم إرسال رسالة تشخيصية تحتوي على سلسلة أقصر من المتوقع، يمكننا الحصول على لقطة لبعض البيانات في كومة diagnosticd، مما قد يساعد في تحقيق تنفيذ كود في العملية.
للبناء، شغّل make. راجع أعلى ملف Makefile للاطلاع على خيارات البناء المختلفة.
شغّل الاستغلال بتحديد حجم التسريب في سطر الأوامر:
$ ./xpc-string-leak 0x40
0x2000000000000000 0xe00007ff39bf0992
0x00007fff56858570 0x00007fff7ed23d0e
0x0000000000000000 0x0000000000000000
0x00007fff7ed52be2 0x00007fff7ed29ed6
يجب أن يكون حجم التسريب مضاعفًا للرقم 8 وأن يكون 16 على الأقل.
كود xpc-string-leak مُتاح في الملكية العامة. كمجاملة، أطلب منك إذا أشرت إلى أي من هذا الكود أو استخدمته أن تنسبه إليّ.
اكتشفت هذا الخطأ في أوائل 2018 (يناير أو فبراير)، لكنني نسيت التحقيق فيه حتى مايو. أبلغت آبل بالمشكلة في 9 مايو، وحصلت على الرقم CVE-2018-4248 وتم إصلاحه في iOS 11.4.1 و macOS 10.13.6 في 9 يوليو.
Brandon Azad