Skip to content
KitploitKITPLOIT
OutilsBlog
Soumettre
OutilsBlog
Soumettre

Outils de Hacking, PenTest et Cybersécurité pour votre Arsenal de Sécurité !

Kitploit est un répertoire d'outils de hacking, de cybersécurité et de pentesting. Découvrez les dernières mises à jour des projets pour trouver des vulnérabilités, analyser des systèmes, automatiser les tests et renforcer votre sécurité.

··Flux·Contact·Confidentialité·© 2026 Kitploit

Répertoire d'outils

Catégories

Voir toutes les catégories
Loading categories
CVE-2022-2590-analysis — Analyse approfondie de CVE-2022-2590, une condition de course dans le noyau Linux concernant la mémoire partagée (shmem) permettant une écriture arbitraire dans des pages en lecture seule via userfaultfd et le contournement de FOLL_FORCE/COW. | Kitploit
Outils/GitHubGitHub/hyeonjun17/cve-2022-2590-analysis
Analyse des VulnérabilitésExploitationArticles et RechercheApprentissage et ÉducationExploitation de Binaires
GitHubhyeonjun17/cve-2022-2590-analysis

CVE-2022-2590-analysis

Analyse approfondie de CVE-2022-2590, une condition de course dans le noyau Linux concernant la mémoire partagée (shmem) permettant une écriture arbitraire dans des pages en lecture seule via userfaultfd et le contournement de FOLL_FORCE/COW.

Voir le dépôt
62il y a 2 ansPas encore vérifié

Populaires

Voir tout →

Découvrez les outils les plus utilisés par notre communauté.

Explorer tous les outils

Parcourez notre collection d'outils

Voir tous les outils →
Partager

CVE-2022-2590, Dirty COW restreint à shmem

version du noyau linux : Linux/x86 6.0.0-rc1 (commit 37887783b3fef877bf34b8992c9199864da4afcb)

Introduction :

cette vulnérabilité permet à l'attaquant d'écrire un contenu arbitraire dans une page mémoire partagée en lecture seule en satisfaisant la fonction can_follow_write_pte, qui vérifie FOLL_FORCE, FOLL_COW et pte_dirty, en utilisant UFFDIO_CONTINUE.

Analyse :

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));
}

La fonction ci-dessus détermine si une pte est accessible en écriture.

La pte est considérée accessible en écriture si elle remplit l'une des conditions suivantes :

  1. Les flags de la pte incluent le flag d'écriture.
  2. La variable flags contient les flags FOLL_FORCE et FOLL_COW, et les flags de la pte incluent le flag dirty.

Pour satisfaire la condition 2, les trois flags suivants doivent être définis :

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 avec FOLL_FORCE activé
		if (!this_len) {
			if (!copied)
				copied = -EIO;
			break;
		}

		...
}

La fonction ci-dessus effectue des opérations de lecture/écriture sur /proc/<pid>/mem.

En utilisant cette fonction, vous pouvez atteindre la fonction __get_user_pages avec le flag FOLL_FORCE activé.

Bien que FOLL_FORCE soit destiné à être utilisé dans ptrace, sa nécessité a été prouvée dans le commit suivant : 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;
    }
    

    L'opération OU avec FOLL_COW dans la fonction faultin_page peut être utilisée, ce qui fait partie du correctif pour 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; // essayer à nouveau la page
     			case -EBUSY:
     			case -EAGAIN:
     				ret = 0;
     				fallthrough;
     			case -EFAULT:
     			case -ENOMEM:
     			case -EHWPOISON:
     				goto out;
     			}
     			BUG();
     		} else if (PTR_ERR(page) == -EEXIST) {
        ...
     }
    

    Afin d'atteindre la fonction can_follow_write_pte avec le flag FOLL_COW activé, vous devez appeler la fonction faultin_page dans la fonction __get_user_pages, rediriger vers l'étiquette retry, et appeler à nouveau la fonction faultin_page.

  • pte_dirty

    Cette condition peut être satisfaite grâce au commit suivant : 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); // définir la pte comme sale inconditionnellement
     	if (page_in_cache && !vm_shared)
     		writable = false;
    
     	...
    }
    

    Grâce au correctif ci-dessus, il est possible d'installer une pte pour la page mémoire partagée en lecture seule dans un état sale inconditionnellement.

  •  

    Si ces trois flags sont satisfaits, la fonction can_follow_write_pte retournera vrai.

    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); // obtenir la page mémoire partagée en lecture seule
    
    	...
    
    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);
    }
    

    La fonction follow_page_pte retourne la page mémoire partagée en lecture seule.

    Scénario de compétition (race) :

    Voici le scénario de compétition prouvé par le PoC, qui sera présenté plus tard.

    madvise et readioctl UFFDIO_CONTINUEpwrite
    madvise // supprimer la page
    shmem_fault // défaut de lecture
    handle_userfault
    userfaultfd_continue
    mcontinue_atomic_pte
    ret = shmem_getpage(inode, pgoff, &page, SGP_NOALLOC); // obtenir la page
    mfill_atomic_install_pte
    _dst_pte = pte_mkdirty(_dst_pte); // rendre la pte sale
    set_pte_at(dst_mm, dst_addr, dst_pte, _dst_pte); // installer la ptemem_rw
    access_remote_vm // avec FOLL_FORCE
    __get_user_pages
    can_follow_write_pte // pas de FOLL_COW, retourne 0
    faultin_page
    flags
    retry:
    follow_page_pte
    madvise // supprimer la page
    shmem_fault // défaut de lecture
    handle_userfault
    userfaultfd_continue
    mcontinue_atomic_pte
    ret = shmem_getpage(inode, pgoff, &page, SGP_NOALLOC); // obtenir la page
    mfill_atomic_install_pte
    _dst_pte = pte_mkdirty(_dst_pte); // rendre la pte sale
    set_pte_at(dst_mm, dst_addr, dst_pte, _dst_pte); // installer la pte
    can_follow_write_pte // retourne 1
    copy_to_user(buf, page, this_len) // écrire le contenu dans la page en lecture seule

    Les routines madvise et read sont exécutées deux fois ; la première exécution provoque un retry de __get_user_pages, et la seconde exécution guide follow_page_pte pour qu'elle retourne la page pointée par la pte qui a été rendue sale par mfill_atomic_install_pte.

    PoC et correctif :

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

    Vous pouvez consulter le reproducteur de David Hildenbrand au lien ci-dessus.

    Cependant, comme le reproducteur rend difficile la reconnaissance du scénario de compétition exact, j'ai modifié le reproducteur pour privilégier une exécution plus linéaire, et j'ai également modifié en conséquence le code source du noyau Linux.

    .config :

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

    PoC : (poc.c)

    Correctif : (poc_deayzl.patch)

    Pour les tests, les étapes préliminaires suivantes doivent être effectuées. (Les instructions du reproducteur de david indiquent le chemin du fichier /tmp/foo. Mais tmpfs n'est pas de la mémoire partagée dans la version actuelle, donc l'enregistrement userfaultfd échoue)

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

    Après avoir exécuté le PoC, vous verrez l'écran suivant.

    PoC-result

    Références :

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

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

    correctif lore.kernel 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

    Télécharger l’outil