Skip to content
KitploitKITPLOIT
HerramientasExploitsBlog
Log in
Enviar
HerramientasExploitsBlog
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.

FeedsContactoPrivacidad© 2026 Kitploit

Directorio de Herramientas

Categorías

Ver todas las categorías
Loading categories
CVE-2026-8933 — Prueba de concepto en C para CVE-2026-8933, una escalada de privilegios local en snap-confine que utiliza el congelamiento de cgroups, condiciones de carrera con enlaces simbólicos e inyección de reglas udev para ejecutar comandos como root. | Kitploit
Herramientas/GitHubGitHub/yifengchen-cc/cve-2026-8933
Escalada de PrivilegiosSeguridad de ContenedoresAnálisis de VulnerabilidadesExplotación
GitHubyifengchen-cc/cve-2026-8933

CVE-2026-8933

Prueba de concepto en C para CVE-2026-8933, una escalada de privilegios local en snap-confine que utiliza el congelamiento de cgroups, condiciones de carrera con enlaces simbólicos e inyección de reglas udev para ejecutar comandos como root.

Ver Repositorio
hace 14 díasAú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-2026-8933 — snap-confine set-capabilities 本地提权 PoC

Reproducir el aviso de Qualys «Local Privilege Escalation in set-capabilities versions of snap-confine» (https://cdn2.qualys.com/advisory/2026/07/21/snap-confine-set-capabilities.txt).

在本机(Ubuntu 25.10 / snapd 2.73+ubuntu25.10.1 / 内核 6.17)实测:从 uid 1000 出发,最终由 systemd-udevd 以 root 执行命令,/tmp/pwned 得到 uid=0(root) gid=0(root) groups=0(root),与公告最后演示的效果一致。

复现过程中的踩坑记录、以及第一次评估为什么误判"不可利用",见 NOTES.md。

文件

文件说明
poc.c完整 PoC(单文件)
Makefile构建(链接系统已安装的 libfuse3.so.3.17.4)
include/libfuse3 3.17.4 头文件(匹配系统运行时库)
NOTES.md踩坑记录 + 可达性分析方法论复盘

漏洞原理

cmd/snap-confine/mount-support.c(snapd 2.73):

sc_bootstrap_mount_namespace()      // 第 475 行起
  479:  mkdtemp("/tmp/snap.rootfs_XXXXXX")          <- 目录先归调用者所有
  491:  sc_do_mount(scratch, scratch, NULL, MS_BIND)
  498:  sc_do_mount("none", scratch, NULL, MS_UNBINDABLE)
  503:  sc_do_mount("none", scratch, "tmpfs", ...)
  504:  sc_replicate_base_rootfs(scratch, rootfs_dir, mounts)

sc_replicate_base_rootfs()
  330:  chown(scratch, 0, 0)
  418:  fd = open(full_path, O_CREAT | O_TRUNC, 0644)   <- 缺少 O_NOFOLLOW
  422:  fchown(fd, 0, 0)

Los números de línea provienen del código fuente de snapd 2.73 de esta máquina (cmd/snap-confine/mount-support.c). El aviso de Qualys cita su propia copia del archivo, con los números de línea correspondientes 511 / 523 / 530 / 535 / 536 / 362 / 450 / 454 — las sentencias se corresponden una a una, solo difieren los números de línea.

El archivo local /usr/lib/snapd/snap-confine tiene mode=755 + file capabilities (no es setuid-root), por lo que se ejecuta con el euid del atacante pero posee capacidades casi de root: los directorios y archivos que crea pertenecen al atacante durante un breve periodo de tiempo, y el open() de la línea 418 sigue enlaces simbólicos.

La desensamblación confirma: en esta máquina los flags de open@plt son 0x240 (O_CREAT|O_TRUNC, sin O_NOFOLLOW); en el mismo punto de la versión 2.77.1 ya corregida son 0x20240.

Los seis pasos del PoC

  1. Meter snap-confine en su propio cgroup: systemd-run --user --scope --unit=snap.<snap>.<app>. Ese scope pertenece al usuario actual, cgroup.freeze es escribible, así que se puede dejar pasar a snap-confine llamada a sistema por llamada a sistema.
  2. Congelar en el lugar correcto: con SNAPD_DEBUG=1, snap-confine imprime associated snap application process ... with device cgroup. Esa línea aparece después de adjuntar el programa BPF del cgroup de dispositivos y antes de mkdtemp(). No se puede congelar antes: el verificador BPF devuelve EAGAIN cuando existe una freeze trap, y snap-confine termina directamente. stderr está conectado a un pty (no a una tubería): de lo contrario stdio se almacena en búfer de bloque y el marcador suele vaciarse solo después de mkdtemp() y del bind mount. Además, el scope se inicia con CPUQuota=1%: desde el marcador hasta mkdtemp() solo hay unos cientos de microsegundos de tiempo de CPU, del mismo orden que la latencia de despertar del proceso capturador; la limitación estira esa ventana en tiempo real hasta el orden de los milisegundos, y tras confirmar la congelación se restaura cpu.max a 100% de inmediato.
  3. Sustituir el directorio scratch por un enlace simbólico a nuestro propio directorio: mount(2) sigue los enlaces simbólicos en la ruta de destino, así que el bind/unbindable/tmpfs de snap-confine acaba todo en <scratch>.dA, mientras que el bucle de copia sigue usando la cadena del directorio scratch.
  4. Tras montar el tmpfs (ns_mount_count(sc, .dA) >= 2), redirigir el enlace simbólico a <scratch>.dB, y colocar en dB enlaces simbólicos con el mismo nombre que los archivos regulares de nivel superior del rootfs base, apuntando a /run/udev/rules.d/97-cve-2026-8933-<pid>-<rand>.rules. En ese momento la resolución de rutas del bucle de copia recae en dB — un directorio normal que controlamos por completo. El tmpfs se queda en dA y ya no nos estorba. Nota: cuando en .dA solo hay 1 montaje, es un bind mount ( /tmp ya es tmpfs, tras el bind el fstype sigue siendo tmpfs); hay que esperar a que aparezca el segundo montaje (el tmpfs real) para redirigir, de lo contrario el mount --make-unbindable de snap-confine fallará con EINVAL porque la ruta ya no es la raíz de un montaje.
  5. Ganar la ventana entre open()/fchown(): el bucle de copia ejecuta sobre el primer archivo regular open(O_CREAT|O_TRUNC) (sigue el enlace simbólico y crea el archivo como root bajo /run/udev/rules.d), y después fchown(fd,0,0). En ese instante el archivo todavía nos pertenece. El PoC usa dos medios a la vez:
    • Ruta principal: seguir usando el freezer para dejar pasar aproximadamente una llamada a sistema cada vez; en cuanto stat() vea el archivo y el uid siga siendo el nuestro, hacer de inmediato chmod 0666 + escribir la regla udev (en ese momento snap-confine está congelado y la ventana queda "abierta de par en par");
    • Respaldo: varios procesos spinner ocupan la misma ventana en un bucle activo de chmod(). Cuando el spinner la gana, el archivo es propiedad de root pero con modo 0666 — los bits de modo se conservan tras fchown(0,0), así que la ruta principal también puede terminar de escribirlo (el código comprueba "si se puede escribir", no "si es el propietario").
  6. Disparar udev: el perfil AppArmor de snap-confine permite /run/udev/** rw. Montar/desmontar un sistema de archivos FUSE hace que el kernel emita un uevent add para el subsistema bdi, y systemd-udevd ejecuta entonces como root el PROGRAM= de la regla.

Uso

make
./poc --diagnose            # 只读自检,不改动任何系统状态
./poc                       #  exploit,默认最多 5 次尝试
./poc --attempts 8          # 增加尝试次数
./poc --file default256.png # 只用 base rootfs 里的这一个普通文件
sudo ./poc --cleanup        # 清理(仅在异常中断留下残留时需要)

Parámetros:

ParámetroDescripción
--snap NAMEQué snap usar para el rootfs (por defecto firefox, también se puede usar la variable de entorno CVE8933_SNAP)
--app NAMELa app de ese snap (por defecto se toma la primera bajo apps: en meta/snap.yaml)
--file NAMEUsar solo este archivo regular del rootfs base; si no hay coincidencia, sale con error
--attempts NNúmero de intentos (por defecto 5)
--step US / --step2 USTiempo de CPU por fragmento en cada una de las dos fases (microsegundos), para depuración
--keepConservar los directorios <scratch>.dA / .dB y los enlaces simbólicos (para depuración)
--diagnoseAutodiagnóstico de solo lectura
--cleanupLimpiar residuos históricos (requiere root)

El registro se escribe en /tmp/cve8933.log, y la salida de depuración de snap-confine se escribe en /tmp/cve8933-debug.txt.

CVE8933_PAYLOAD_CMD='<comando de shell>' permite reemplazar el cuerpo del payload (para demostrar la ejecución de otros comandos como root); en ese caso no se limpia automáticamente; el comando personalizado todavía debe escribir /tmp/pwned, de lo contrario el PoC considerará que esta vez no tuvo éxito.

Resultados de las pruebas reales

$ ./poc
[+] WIN: uid=0(root) gid=0(root) groups=0(root)
[+] exploit succeeded, evidence in /tmp/pwned

/tmp/cve8933.log 中对应的一次成功过程(2026-10-23,改动后复测):

Descargar herramienta