Skip to content
KitploitKITPLOIT
أدواتعمليات الاستغلالالمدونة
Log in
إرسال
أدواتعمليات الاستغلالالمدونة
إرسال

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

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

عرض المستودع
6873منذ 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 من الكود المصدري عند إصلاح هذه المشكلة يلخص الأمر بشكل جيد:

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

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

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:

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 في بداية البيانات المعادة.

تنزيل الأداة