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

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

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

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

دليل الأدوات

الفئات

عرض جميع الفئات
Loading categories
CVE-2022-2590-analysis — تحليل متعمق لثغرة CVE-2022-2590، وهي متغيّر من ثغرة Dirty COW في shmem بنواة Linux، بما في ذلك السبب الجذري، وحالة التسابق (race condition)، واستغلال إثبات المفهوم (proof-of-concept). | Kitploit
أدوات/GitHubGitHub/moonnull/cve-2022-2590-analysis
تحليل الثغرات الأمنيةالاستغلالاستغلال الملفات الثنائية
GitHubmoonnull/cve-2022-2590-analysis

CVE-2022-2590-analysis

تحليل متعمق لثغرة CVE-2022-2590، وهي متغيّر من ثغرة Dirty COW في shmem بنواة Linux، بما في ذلك السبب الجذري، وحالة التسابق (race condition)، واستغلال إثبات المفهوم (proof-of-concept).

عرض المستودع
منذ 0 أياملم تتم المراجعة بعد

الأكثر شعبية

عرض الكل →

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

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

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

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

CVE-2022-2590, Dirty COW مقتصر على shmem

إصدار نواة لينكس: Linux/x86 6.0.0-rc1 (commit 37887783b3fef877bf34b8992c9199864da4afcb)

مقدمة:

تسمح هذه الثغرة للمهاجم بكتابة محتوى عشوائي إلى صفحة ذاكرة مشتركة للقراءة فقط عبر استيفاء شروط الدالة can_follow_write_pte، التي تتحقق من FOLL_FORCE وFOLL_COW وpte_dirty، باستخدام UFFDIO_CONTINUE.

التحليل:

root@kitploit:~
/*
 * FOLL_FORCE can write to even unwritable pte's, but only
 * after we've gone through a COW cycle and they are dirty.
 */
static inline bool can_follow_write_pte(pte_t pte, unsigned int flags)
{
	return pte_write(pte) ||
		((flags & FOLL_FORCE) && (flags & FOLL_COW) && pte_dirty(pte));
}

تحدد الدالة أعلاه ما إذا كان pte قابلاً للكتابة.

يُعتبر pte قابلاً للكتابة إذا استوفى أحد الشروط التالية:

  1. تتضمن أعلام pte علم القابلية للكتابة.
  2. يحتوي متغير flags على العلمين FOLL_FORCE وFOLL_COW، وتتضمن أعلام pte علم الإتساخ (dirty).

لاستيفاء الشرط 2، يجب تعيين الأعلام الثلاثة التالية:

  1. FOLL_FORCE

    root@kitploit:~
    static ssize_t mem_rw(struct file *file, char __user *buf,
    			size_t count, loff_t *ppos, int write)
    {
    	...
    
    	flags = FOLL_FORCE | (write ? FOLL_WRITE : 0);
    
    	while (count > 0) {
    		size_t this_len = min_t(size_t, count, PAGE_SIZE);
    
    		if (write && copy_from_user(page, buf, this_len)) {
    			copied = -EFAULT;
    			break;
    		}
    
    		this_len = access_remote_vm(mm, addr, page, this_len, flags); // __get_user_pages with FOLL_FORCE on
    		if (!this_len) {
    			if (!copied)
    				copied = -EIO;
    			break;
    		}
    
    		...
    }
    

تقوم الدالة أعلاه بعمليات القراءة/الكتابة على /proc/<pid>/mem.

باستخدام هذه الدالة، يمكنك الوصول إلى الدالة __get_user_pages مع تفعيل العلم FOLL_FORCE.

على الرغم من أن FOLL_FORCE مخصّص للاستخدام في ptrace، فقد ثُبتت ضرورته في الالتزام التالي: https://github.com/torvalds/linux/commit/f511c0b17b081562dca8ac5061dfa86db4c66cc2

  • FOLL_COW

    root@kitploit:~
    /*
    * mmap_lock must be held on entry.  If @locked != NULL and *@flags
    * does not include FOLL_NOWAIT, the mmap_lock may be released.  If it
    * is, *@locked will be set to 0 and -EBUSY returned.
    */
    static int faultin_page(struct vm_area_struct *vma,
    	unsigned long address, unsigned int *flags, bool unshare,
    	int *locked)
    {
      ...
    
      /*
      * The VM_FAULT_WRITE bit tells us that do_wp_page has broken COW when
      * necessary, even if maybe_mkwrite decided not to set pte_write. We
      * can thus safely do subsequent page lookups as if they were reads.
      * But only do so when looping for pte_write is futile: in some cases
      * userspace may also be wanting to write to the gotten user page,
      * which a read fault here might prevent (a readonly page might get
      * reCOWed by userspace write).
      */
      if ((ret & VM_FAULT_WRITE) && !(vma->vm_flags & VM_WRITE))
        *flags |= FOLL_COW;
      return 0;
    }
    

    يمكن استخدام عملية OR مع FOLL_COW في الدالة faultin_page، وهي جزء من التصحيح الخاص بـ Dirty COW.

    root@kitploit:~
    static long __get_user_pages(struct mm_struct *mm,
     		unsigned long start, unsigned long nr_pages,
     		unsigned int gup_flags, struct page **pages,
     		struct vm_area_struct **vmas, int *locked)
     {
     	...
     retry:
     		/*
     		 * If we have a pending SIGKILL, don't keep faulting pages and
     		 * potentially allocating memory.
     		 */
     		if (fatal_signal_pending(current)) {
     			ret = -EINTR;
     			goto out;
     		}
     		cond_resched();
    
     		page = follow_page_mask(vma, start, foll_flags, &ctx); // can_follow_write_pte
     		if (!page || PTR_ERR(page) == -EMLINK) {
     			ret = faultin_page(vma, start, &foll_flags,
     					   PTR_ERR(page) == -EMLINK, locked); // flags |= FOLL_COW
     			switch (ret) {
     			case 0:
     				goto retry; // try follow page again
     			case -EBUSY:
     			case -EAGAIN:
     				ret = 0;
     				fallthrough;
     			case -EFAULT:
     			case -ENOMEM:
     			case -EHWPOISON:
     				goto out;
     			}
     			BUG();
     		} else if (PTR_ERR(page) == -EEXIST) {
        ...
     }
    

    من أجل الوصول إلى الدالة can_follow_write_pte مع تفعيل العلم FOLL_COW، تحتاج إلى استدعاء الدالة faultin_page داخل الدالة __get_user_pages، والانتقال إلى العلامة retry، ثم استدعاء الدالة faultin_page مرة أخرى.

  • pte_dirty

    يمكن استيفاء هذا الشرط بفضل الالتزام التالي: https://github.com/torvalds/linux/commit/9ae0f87d009ca6c4aab2882641ddfc319727e3db

    root@kitploit:~
    /*
    * Install PTEs, to map dst_addr (within dst_vma) to page.
    *
    * This function handles both MCOPY_ATOMIC_NORMAL and _CONTINUE for both shmem
    * and anon, and for both shared and private VMAs.
    */
    int mfill_atomic_install_pte(struct mm_struct *dst_mm, pmd_t *dst_pmd,
    		     struct vm_area_struct *dst_vma,
    		     unsigned long dst_addr, struct page *page,
    		     bool newly_allocated, bool wp_copy)
    {
     	...
    
     	_dst_pte = mk_pte(page, dst_vma->vm_page_prot);
     	_dst_pte = pte_mkdirty(_dst_pte); // set pte dirty unconditionally
     	if (page_in_cache && !vm_shared)
     		writable = false;
    
     	...
    }
    

    بفضل التصحيح أعلاه، يمكن تثبيت pte لصفحة الذاكرة المشتركة للقراءة فقط في حالة متسخة (dirty) دون أي شروط.

  •  

    إذا تم استيفاء هذه الأعلام الثلاثة، فستُرجع الدالة can_follow_write_pte القيمة true.

    root@kitploit:~
    static struct page *follow_page_pte(struct vm_area_struct *vma,
    		unsigned long address, pmd_t *pmd, unsigned int flags,
    		struct dev_pagemap **pgmap)
    {
    	...
        
    	// true && !true == false
    	if ((flags & FOLL_WRITE) && !can_follow_write_pte(pte, flags)) {
    		pte_unmap_unlock(ptep, ptl);
    		return NULL;
    	}
    
    	page = vm_normal_page(vma, address, pte); // get read-only shared memory page
    
    	...
    
    out:
    	pte_unmap_unlock(ptep, ptl);
    	return page;
    no_page:
    	pte_unmap_unlock(ptep, ptl);
    	if (!pte_none(pte))
    		return NULL;
    	return no_page_table(vma, flags);
    }
    

    تُرجع الدالة follow_page_pte صفحة الذاكرة المشتركة للقراءة فقط.

    سيناريو السباق:

    فيما يلي سيناريو السباق الذي أثبته الـ PoC، والذي سيُقدَّم لاحقًا.

    madvise والقراءةUFFDIO_CONTINUE ioctlpwrite
    madvise // zap the page
    shmem_fault // read fault
    handle_userfault
    userfaultfd_continue
    mcontinue_atomic_pte
    ret = shmem_getpage(inode, pgoff, &page, SGP_NOALLOC); // get page
    mfill_atomic_install_pte
    _dst_pte = pte_mkdirty(_dst_pte); // make pte dirty
    set_pte_at(dst_mm, dst_addr, dst_pte, _dst_pte); // install ptemem_rw
    access_remote_vm // with FOLL_FORCE
    __get_user_pages
    can_follow_write_pte // no FOLL_COW, return 0
    faultin_page
    flags
    retry:
    follow_page_pte
    madvise // zap the page
    shmem_fault // read fault
    handle_userfault
    userfaultfd_continue
    mcontinue_atomic_pte
    ret = shmem_getpage(inode, pgoff, &page, SGP_NOALLOC); // get page
    mfill_atomic_install_pte
    _dst_pte = pte_mkdirty(_dst_pte); // make pte dirty
    set_pte_at(dst_mm, dst_addr, dst_pte, _dst_pte); // install pte
    can_follow_write_pte // return 1
    copy_to_user(buf, page, this_len) // write content to read-only page

    الـ PoC والتصحيح:

    https://www.openwall.com/lists/oss-security/2022/08/15/1

    يمكنك الاطلاع على reproducer الخاص بـ David Hildenbrand في الرابط أعلاه.

    غير أنه نظرًا لأن الـ reproducer يجعل من الصعب تمييز سيناريو السباق بدقة، فقد عدّلت الـ reproducer لتفضيل تنفيذ أكثر خطية، وبناءً عليه عدّلت الكود المصدري لنواة لينكس أيضًا.

    .config:

    root@kitploit:~
    ...
    CONFIG_USERFAULTFD=y
    CONFIG_HAVE_ARCH_USERFAULTFD_WP=y
    CONFIG_HAVE_ARCH_USERFAULTFD_MINOR=y
    ...
    

    الـ PoC: (poc.c)

    التصحيح: (poc_deayzl.patch)

    من أجل الاختبار، يجب تنفيذ الخطوات التمهيدية التالية. (تشير التعليمات في reproducer الخاص بـ david إلى أن مسار الملف هو /tmp/foo. لكن tmpfs ليست ذاكرة مشتركة في الإصدار الحالي، لذلك يفشل تسجيل userfaultfd).

    root@kitploit:~
    sudo -s
    echo "asdf" > /dev/shm/foo
    chmod 0404 /dev/shm/foo
    exit
    

    بعد تشغيل الـ PoC، ستشاهد الشاشة التالية.

    PoC-result

    المراجع:

    commit: https://github.com/torvalds/linux/commit/5535be3099717646781ce1540cf725965d680e7b

    lore.kernel patch v1: https://lore.kernel.org/linux-mm/[email protected]/#r

    lore.kernel patch v2: https://lore.kernel.org/all/[email protected]/T/#u

    openwall: https://www.openwall.com/lists/oss-security/2022/08/08/1

    openwall2: https://lists.openwall.net/linux-kernel/2022/08/08/418

    تنزيل الأداة