Skip to content
KitploitKITPLOIT
HerramientasBlog
Enviar
HerramientasBlog
Enviar

¡Herramientas de Hacking, PenTest y Ciberseguridad para tu Arsenal de Seguridad!

Kitploit es un directorio de herramientas de hacking, ciberseguridad y pentesting. Descubre las últimas actualizaciones de proyectos para encontrar vulnerabilidades, analizar sistemas, automatizar pruebas y fortalecer tu seguridad.

··Feeds·Contacto·Privacidad·© 2026 Kitploit

Directorio de Herramientas

Categorías

Ver todas las categorías
Loading categories
CVE-2022-2590-analysis — Análisis en profundidad de CVE-2022-2590, una condición de carrera en el kernel de Linux en la memoria compartida (shmem) que permite escritura arbitraria en páginas de solo lectura mediante userfaultfd y la omisión de FOLL_FORCE/COW. | Kitploit
Herramientas/GitHubGitHub/hyeonjun17/cve-2022-2590-analysis
Análisis de VulnerabilidadesExplotaciónPapers e InvestigaciónAprendizaje y EducaciónExplotación de Binarios
GitHubhyeonjun17/cve-2022-2590-analysis

CVE-2022-2590-analysis

Análisis en profundidad de CVE-2022-2590, una condición de carrera en el kernel de Linux en la memoria compartida (shmem) que permite escritura arbitraria en páginas de solo lectura mediante userfaultfd y la omisión de FOLL_FORCE/COW.

Ver Repositorio
62hace 2 añosAún no revisado

Más Populares

Ver todos →

Descubre las herramientas más usadas por nuestra comunidad.

Explora todas las herramientas

Explora nuestra colección de herramientas

Ver todas las herramientas →
Compartir

CVE-2022-2590, Dirty COW restringido a shmem

versión del kernel de Linux: Linux/x86 6.0.0-rc1 (commit 37887783b3fef877bf34b8992c9199864da4afcb)

Introducción:

esta vulnerabilidad permite al atacante escribir contenido arbitrario en una página de memoria compartida de solo lectura al satisfacer la función can_follow_write_pte, que comprueba FOLL_FORCE, FOLL_COW y pte_dirty, mediante UFFDIO_CONTINUE.

Análisis:

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 función anterior determina si una pte es escribible.

La pte se considera escribible si cumple una de las siguientes condiciones:

  1. Los flags de la pte incluyen el flag de escritura.
  2. La variable flags contiene los flags FOLL_FORCE y FOLL_COW, y los flags de la pte incluyen el flag dirty.

Para satisfacer la condición 2, es necesario establecer los siguientes tres flags:

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

		...
}

La función anterior realiza operaciones de lectura/escritura sobre /proc/<pid>/mem.

Usando esta función, se puede llegar a la función __get_user_pages con el flag FOLL_FORCE activado.

Aunque FOLL_FORCE está pensado para usarse en ptrace, su necesidad ha quedado demostrada en el siguiente commit: 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;
    }
    

    Se puede usar la operación OR con FOLL_COW en la función faultin_page, la cual forma parte del parche para 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) {
        ...
     }
    

    Para llegar a la función can_follow_write_pte con el flag FOLL_COW activado, es necesario llamar a la función faultin_page dentro de la función __get_user_pages, redirigir a la etiqueta retry y volver a llamar a la función faultin_page.

  • pte_dirty

    Esta condición puede cumplirse gracias al siguiente commit: 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;
    
     	...
    }
    

    Debido al parche anterior, es posible instalar una pte en estado dirty para la página de memoria compartida de solo lectura de forma incondicional.

  •  

    Si esos tres flags se cumplen, la función can_follow_write_pte devolverá 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);
    }
    

    La función follow_page_pte devuelve la página de memoria compartida de solo lectura.

    Escenario de carrera:

    El siguiente es el escenario de carrera demostrado por el PoC, que se presentará más adelante.

    madvise y readUFFDIO_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

    Las rutinas madvise y read se ejecutan dos veces; la primera ejecución induce un retry de __get_user_pages, y la segunda ejecución hace que follow_page_pte devuelva la página apuntada por la pte que ha sido marcada como dirty por mfill_atomic_install_pte.

    PoC y Parche:

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

    Puedes consultar el reproductor de David Hildenbrand en el enlace anterior.

    Sin embargo, dado que el reproductor dificulta reconocer el escenario de carrera exacto, modifiqué el reproductor para favorecer una ejecución más lineal y, en consecuencia, también modifiqué el código fuente del kernel de Linux.

    .config:

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

    PoC: (poc.c)

    Parche: (poc_deayzl.patch)

    Para realizar las pruebas, deben seguirse los siguientes pasos preliminares. (Las instrucciones del reproductor de david indican que la ruta del archivo es /tmp/foo. Pero tmpfs no es memoria compartida en la versión actual, por lo que el registro de userfaultfd falla)

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

    Después de ejecutar el PoC, verás la siguiente pantalla.

    PoC-result

    Referencias:

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

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

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

    Descargar herramienta