Skip to content
KitploitKITPLOIT
أدواتالمدونة
إرسال
أدواتالمدونة
إرسال

أدوات الاختراق واختبار الاختراق والأمن السيبراني لترسانتك الأمنية!

Kitploit هو دليل لأدوات الاختراق والأمن السيبراني واختبار الاختراق. اكتشف آخر تحديثات المشاريع للعثور على الثغرات وتحليل الأنظمة وأتمتة الاختبارات وتعزيز أمنك.

··الخلاصات·اتصال·الخصوصية·© 2026 Kitploit

دليل الأدوات

الفئات

عرض جميع الفئات
Loading categories
CVE-2022-42864 — إثبات المفهوم لحالة السباق CVE-2022-42864 في IOHIDFamily | Kitploit
أدوات/GitHubGitHub/muirey03/cve-2022-42864
تحليل الثغرات الأمنيةالاستغلالاستغلال الملفات الثنائية
GitHubmuirey03/cve-2022-42864

CVE-2022-42864

إثبات المفهوم لحالة السباق CVE-2022-42864 في IOHIDFamily

عرض المستودع
6872منذ 3 سنواتتمت المراجعة من قبل Kitploit

الأكثر شعبية

عرض الكل →

اكتشف الأدوات الأكثر استخدامًا من قبل مجتمعنا.

استكشف جميع الأدوات

تصفح مجموعتنا من الأدوات

عرض جميع الأدوات →
مشاركة

CVE-2022-42864: الكوكيز الشيطانية

ما هو هذا المستودع؟

هذا هو استغلال إثبات المفهوم (غير المكتمل) الخاص بي لثغرة CVE-2022-42864، وهي ثغرة من نوع زمن الفحص-زمن الاستخدام (TOCTOU) في IOHIDFamily تم إصلاحها في iOS 16.2 / macOS Ventura 13.1.

ما هي حالة إثبات المفهوم؟

يحقق الاستغلال حاليًا نفس أولية "التحرير التعسفي (arbitrary kfree)" المستخدمة في استغلال multicast_bytecopy. ومع ذلك، فقد تم تحصين مسار الاستغلال اللاحق الخاص بـ multicast_bytecopy بشدة، لذا فهذا ليس استغلالًا كاملًا، بل يوضح فقط مدى خطورة المشكلة.

هل يجب عليّ تشغيل هذا؟

إذا كنت بحاجة إلى السؤال، فالإجابة لا. هذا لا يفعل أي شيء مفيد، بل يسبب فقط انهيار النواة (kernel panic). لا أتحمل أي مسؤولية عن أي فقدان للبيانات أو عدم استقرار قد يسببه هذا الكود.

الثغرة

تعليق Apple من الكود المصدري عند إصلاح هذه المشكلة يلخص الأمر بشكل جيد:

root@kitploit:~
// Find the number of cookies in the data. The data from elementData is shared with user space and may change at any time.

دعونا نلقي نظرة على الدالة قبل التصحيح (حاولت تسمية الأسطر ذات الصلة):

root@kitploit:~
IOReturn IOHIDDevice::postElementTransaction(const void* elementData, UInt32 dataSize, UInt32 completionTimeout, IOHIDCompletion * completion)
{
    IOReturn ret = kIOReturnError;
    uint32_t   cookies_[kMaxLocalCookieArrayLength];
    uint32_t   *cookies = cookies_;
    uint32_t   cookieCount = 0;
    uint32_t   cookieSize = 0;
    uint32_t   dataOffset = 0;
    uint8_t    *data = (uint8_t*)elementData;
    IOMemoryDescriptor *elementDesc = getMemoryWithCurrentElementValues();
    require(_elementArray && elementDesc, fail);

    WORKLOOP_LOCK;

    // Find the number of cookies in the data. Check that all cookies are valid elements.                   [1]
    while (dataOffset < dataSize) {
        const IOHIDElementValueHeader *headerPtr = (const IOHIDElementValueHeader *)(data + dataOffset);
        IOHIDElementPrivate *element = GetElement(headerPtr->cookie);
        if (!element) {
            HIDDeviceLogError("Could not find element for cookie: %d", headerPtr->cookie);
            ret = kIOReturnAborted;
            goto fail;
        }
        cookieCount++;

        require_noerr_action(os_add3_overflow(dataOffset, headerPtr->length, sizeof(IOHIDElementValueHeader), &dataOffset), fail, HIDDeviceLogError("Overflow iterating cookie data buffer %u %u", dataOffset, headerPtr->length));
    }
    // Data isn't as large as expected, don't overrun, just abort
    if (dataOffset != dataSize) {   //                                                                      [2]
        HIDDeviceLogError("Cookie data buffer is smaller than expected. %u vs. %u",
                        (unsigned int)dataSize, (unsigned int)dataOffset);
        ret = kIOReturnAborted;
        goto fail;
    }
    dataOffset = 0;

    require_noerr_action(os_mul_overflow(cookieCount, sizeof(uint32_t), &cookieSize),
                        fail,
                        HIDDeviceLogError("Overflow calculating cookieSize"));

    cookies = (cookieCount <= kMaxLocalCookieArrayLength) ? cookies : (uint32_t*)IOMallocData(cookieSize); // [3]

    if (cookies == NULL) {
        ret = kIOReturnNoMemory;
        goto fail;
    }

    // Update the elements, this replaced the shared kernel-user shared memory.
    for (size_t index = 0; dataOffset < dataSize; ++index) {    //                                          [4]
        const IOHIDElementValueHeader *headerPtr;
        IOHIDElementPrivate *element;
        OSData *elementVal;

        headerPtr = (const IOHIDElementValueHeader *)(data + dataOffset);
        element = GetElement(headerPtr->cookie);
        dataOffset += headerPtr->length + sizeof(IOHIDElementValueHeader);

        elementVal = OSData::withBytesNoCopy((void*)headerPtr->value,
                                            headerPtr->length); //                                          [5]
        require_action(elementVal, fail, ret = kIOReturnNoMemory);
        element->setDataBits(elementVal);
        elementVal->release();

        cookies[index] = headerPtr->cookie; //                                                              [6]
    }

    // Actually post elements
    ret = postElementValues((IOHIDElementCookie *)cookies, (UInt32)cookieCount, 0, completionTimeout, completion);

fail:
    WORKLOOP_UNLOCK;
    if (cookies != &cookies_[0]) {
        IOFreeData(cookies, cookieSize);
    }

    return ret;
}
  • الحلقة عند [1] تعدّ عدد IOHIDElementValues في المخزن المؤقت، وتخزّن هذا العدد في cookieCount.
  • الفحص عند [2] (بالاشتراك مع شرط حلقة while) يضمن أن حقل length لكل ترويسة لا يمتد خارج حدود مخزن elementData المؤقت (كما لا يمكن أن يقصر عن نهاية المخزن، على الرغم من أن هذا أقل أهمية).
  • بمجرد عدّ جميع العناصر والتحقق من سلامتها بواسطة هذه الحلقة، يتم تخصيص مخزن cookies مؤقت في الكومة عند [3] بحجم cookieCount * 4 (أو يُستخدم مخزن مكدس إذا كان cookieCount صغيرًا بما يكفي).
  • ثم تقوم حلقة ثانية عند [4] بتمريرة ثانية عبر المخزن، لتحليل IOHIDElementValues مرة أخرى.
  • يتم إنشاء كائنات OSData لاحتواء قيمة كل عنصر عند [5]، باستخدام حقل length الذي تم التحقق منه في الحلقة الأولى.
  • عند [6]، تتم كتابة cookie لكل عنصر في مصفوفة cookies المخصصة عند [3].

إذن ما هي المشكلة؟ تتصرف هذه الدالة بشكل صحيح تمامًا عندما تكون elementData غير متغيرة، وتظهر المشكلة عند استدعاء الدالة بذاكرة مشتركة. هنا تأتي دالة DriverKit IOHIDInterface::SetElementValues_Impl:

root@kitploit:~
kern_return_t
IMPL(IOHIDInterface, SetElementValues)
{
    IOReturn ret = kIOReturnError;
    UInt8 *values = NULL;
    IOBufferMemoryDescriptor *md = NULL;
    
    md = OSDynamicCast(IOBufferMemoryDescriptor, elementValues);
    require_action(md && count, exit, ret = kIOReturnBadArgument);

    values = (UInt8 *)md->getBytesNoCopy();
    
    // Post the data to the device
    ret = _owner->postElementTransaction(values, (UInt32)md->getLength());
    require_noerr_action(ret, exit, HIDServiceLogError("postElementValues failed: 0x%x", ret));

exit:
    return ret;
}

هنا، يتم استدعاء postElementTransaction مع md->getBytesNoCopy()، وهي ذاكرة مشتركة مع مساحة المستخدم، مما ينتهك الافتراض بأن elementData غير متغيرة. يمكن أن يتغير محتوى مخزن elementData بعد الحلقة عند [1]، ولكن قبل الحلقة عند [4]، فماذا يعني هذا للمهاجم؟

هناك طريقتان يمكن للمهاجم استغلال ذلك من خلالهما:

  • الأولى هي تبديل length لترويسة IOHIDElementValueHeader صغيرة في نهاية المخزن إلى قيمة أكبر بكثير. هذا يعني أنه عند إنشاء OSData عند [5]، ستمتد بعيدًا خارج حدود مخزن elementData، مما يسمح للمهاجم بقراءة بيانات خارج الحدود باستخدام IOHIDInterface::GetElementValues_Impl.
  • الثانية هي تبديل length لترويسة IOHIDElementValueHeader كبيرة في بداية المخزن إلى قيمة أصغر بكثير. هذا سيجعل الحلقة عند [4] تحلل عددًا أكبر بكثير من الترويسات مما تم عده في الأصل في الحلقة عند [1]، لذلك عند كتابة الكوكيز في مصفوفة cookies عند [6]، سوف تفيض خارج المصفوفة لأن index لا يتم التحقق منه أبدًا مقابل cookieCount.

عمليًا، يسمح هذا للمهاجم بقراءة بيانات كومة النواة خارج الحدود بحجم تعسفي، وكتابة بيانات تعسفية (مرة أخرى بحجم تعسفي) خارج الحدود إلى كومة النواة. هاتان أوليتان قويتان.

الفوز بالتسابق

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

بالنسبة لنسخة القراءة خارج الحدود (OOB read)، أضع ترويسة IOHIDElementValueHeader إضافية بعد الترويسة التي أقوم بتبديل length الخاصة بها، مع ضبط value على الثابت المعروف 0xD1AB011CAC1DF00D. بعد ذلك، عند قراءة قيمة العنصر مرة أخرى، أعلم أنني فزت بالتسابق إذا رأيت الترويسة المألوفة 0xD1AB011CAC1DF00D في بداية البيانات المعادة.

بالنسبة لنسخة الكتابة خارج الحدود (OOB write)، أضع ترويسة IOHIDElementValueHeader واحدة بعد الترويسة التي أقوم بتبديل length الخاصة بها، ولكن قبل الترويسات التي ستفيض cookies الخاصة بها، هذه المرة بقيمة 0xD15EA5ED المعروفة. سيتم تغليف هذه الترويسة داخل value للعنصر الأكبر في حالة عدم فوزنا بالتسابق، لذلك لن يتم تحليل الترويسة وتعيين قيمة العنصر إلى 0xD15EA5ED إلا إذا فزنا بالتسابق. من خلال قراءة قيمة العنصر مرة أخرى، أعرف ما إذا كنتُ ناجحًا.

إصلاح Apple

لإصلاح المشكلة، اختارت Apple إضافة حلقة ثالثة بين الحلقة [1] والحلقة [4]، للتحقق من كل حقل length، ثم تخزينه مؤقتًا في مصفوفة جديدة dataLengths، مع ضمان عدم تغير عدد العناصر. تستخدم الحلقة النهائية الأطوال المخزنة مؤقتًا في حساباتها، متجنبةً قراءة المخزن مرة أخرى.

مشاكل الاستغلال

العقبة الرئيسية التي يجب التغلب عليها عند استغلال هذه المشكلة هي أن المخزن الذي نفيض منه ينتمي إلى KHEAP_DATA_BUFFERS، لذا فإن أهداف الاستغلال محدودة. في إثبات المفهوم هذا، اخترت استهداف ترويسات kmsg، لأنها واحدة من البنى القليلة جدًا في KHEAP_DATA_BUFFERS التي تحتوي على مؤشرات للنواة. أولية "التحرير التعسفي (arbitrary kfree)" التي حصلت عليها باستخدام هذا النهج هي نفس الأولية المستخدمة في استغلال multicast_bytecopy، ومع ذلك فإن مصفوفة IOSurfaceClient أصبحت الآن محمية بـ PAC، وتحتاج العملاء المزورون إلى مؤشر صالح يعود إلى IOSurfaceRootUserClient الذي أنشأها، مما يجعل هذا الهدف لم يعد مرغوبًا للقراءة/الكتابة في النواة.

البناء والتثبيت

لم تجعل Apple بناء وتثبيت امتدادات DriverKit المخصصة أمرًا سهلًا للغاية، خاصةً بدون حساب Apple Developer مدفوع، لكنه ممكن.

قبل أن تبدأ، أنصحك بـ:

  • تعطيل SIP
  • ضبط وسائط الإقلاع (boot-args) على amfi_get_out_of_my_way=1 cs_enforcement_disable=1
  • تشغيل systemextensionsctl developer on
  • إعادة تشغيل جهاز الكمبيوتر الخاص بك

بعد ذلك، يجب أن تكون قادرًا على تحديد فريق المطورين الخاص بك في إعدادات مشروع Xcode وسيتم بناء المشروع بنجاح. يمكنك بعد ذلك تشغيل HIDDriverLoader، واستخدام "Install Dext" لتثبيت امتداد DriverKit و"Trigger Exploit" لتشغيل الاستغلال، كما خمنت.

إذا فشل ذلك، يمكنك أيضًا محاولة البناء دون توقيع باستخدام:

root@kitploit:~
xcodebuild build CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO

ثم التوقيع يدويًا باستخدام:

root@kitploit:~
codesign -fs self-sign-cert --entitlements HIDDriverLoader/HIDDriverLoader.entitlements build/Release/HIDDriverLoader.app/Contents/MacOS/HIDDriverLoader
codesign -fs self-sign-cert --entitlements HIDDriver/HIDDriver.entitlements build/Release/HIDDriverLoader.app/Contents/Library/SystemExtensions/*.dext/*.driver

مع استبدال self-sign-cert باسم شهادة موقعة ذاتيًا في سلسلة المفاتيح الخاصة بك.

شكرًا لك على القراءة :)

تنزيل الأداة