Saltar al contenido
cve-2026-95834 ·
Media · 25 de septiembre de 2026

Uso después de la liberación (use-after-free) en el protocolo de arrastrar y soltar de kitty cuando un elemento de origen se aborta en mitad de la transferencia

S0
Secur0 CNA
cve-2026-95834

Descripción

Uso después de liberación (CWE-416) en drag_remote_file_data() (kitty/dnd.c), en la ruta de origen de arrastre del protocolo de arrastrar y soltar, en kitty desde 0.47.0 y anterior a 0.49.0, que permite a un programa que escribe en el terminal hacer que el terminal lea y escriba en memoria dinámica ya liberada, porque la función conserva un puntero a un DragRemoteItem dentro de un array del que no es propietaria, llama a toplevel_data_for_drag() o a subdir_data_for_drag(), y a continuación sigue utilizando ese puntero.

Esos auxiliares, y add_payload() y populate_dir_entries() a las que llaman, notifican los errores mediante la macro abrt(), que se expande a cancel_drag() seguido de un return simple, y cancel_drag() llama a drag_free_offer(), que libera el array al que apunta el puntero. Los auxiliares devuelven void, de modo que quien los llama no recibe indicación alguna de que se ha producido el desmontaje, y pasa a llamar a all_children_complete() sobre el puntero liberado, lo que lo desreferencia, y después a escribir a través de él. En la rama de nivel superior, poner a cero los punteros de la propia estructura propietaria durante el desmontaje no sirve de nada, porque el puntero obsoleto es una variable local del marco de pila de la función llamante.

No todos los abortos alcanzan la desreferencia. La función llamante la protege con un indicador que sólo se activa cuando la solicitud no transporta carga y no anuncia más datos, y ésa es la misma condición que selecciona el bloque de finalización de add_payload(), de modo que son las rutas de error de ese bloque las que importan. Dos de esas rutas quedan determinadas por completo por los propios bytes del cliente, porque un cliente que declare dos entradas con un mismo nombre hace que la segunda creación falle con EEXIST: la creación del fichero, que emplea O_CREAT con O_EXCL, y la creación del enlace simbólico, que emplea symlinkat(). Los abortos de toplevel_data_for_drag() son alcanzables por la misma vía. Los abortos que transportan carga no pueden alcanzar la desreferencia en absoluto.

Ambas ramas de la función llamante están afectadas. subdir_data_for_drag() pone a NULL el puntero de la función llamante al entrar y sólo lo asigna después de su última ruta de error, de modo que sus propios abortos dejan a la llamante con NULL y quedan detenidos por una comprobación de nulidad, pero después invoca add_payload() con ese puntero ya asignado, y un aborto en ese punto deja a la llamante sosteniendo un nodo hijo liberado dentro del árbol de elementos.

Tipo de vulnerabilidad (CWE)

  • CWE-416: Uso después de liberación

Versiones afectadas

kitty desde 0.47.0 y anterior a 0.49.0. El protocolo de arrastrar y soltar se introdujo en kitty/dnd.c, publicado por primera vez en v0.47.0; las versiones anteriores no contienen el código afectado. Están afectadas todas las versiones desde v0.47.0 hasta v0.48.2, ambas incluidas. Estado por defecto: no afectado.

Puntuación (CVSS 4.0)

Media (4.6)

CVSS:4.0/AV:L/AC:L/AT:N/PR:N/UI:A/VC:N/VI:L/VA:L/SC:N/SI:N/SA:N

Las métricas de impacto son Bajas y no Altas porque la memoria que se escribe tras la liberación no es memoria cuyo contenido controle el atacante, y los campos que se leen a través del puntero liberado quedan fuera de la región que el asignador reutiliza para sus propios metadatos. La interacción del usuario es Activa porque la secuencia requiere que haya un arrastre en curso sobre la ventana, que el usuario ha de iniciar. Los requisitos de ataque son Ninguno: nada del ataque en sí es condicional, y la consecuencia limitada la recogen las métricas de impacto en lugar de contarse por segunda vez en la métrica de requisitos.

La gravedad de este problema reside en el defecto y no en una consecuencia que se derive de él. drag_free_remote_item() pone a cero cada elemento antes de liberar el array que lo contiene, por lo que el campo que se lee a través del puntero liberado devuelve cero y no datos del atacante, y el campo en el que se escribe queda fuera de la región que el asignador reutiliza para sus propios metadatos. Aun así, el comportamiento posterior a una liberación es indefinido y depende del asignador, razón por la cual esto se puntúa como un defecto que merece corregirse y no se descarta. Es un uso después de liberación en una ruta que un cliente alcanza cuando quiere, y Kovid Goyal lo ha corregido en 0.49.0. Se recomienda actualizar.

Solución

Actualizar a kitty 0.49.0 o posterior.

Parche

Commit b54bfbb4, que modifica populate_dir_entries(), add_payload(), toplevel_data_for_drag() y subdir_data_for_drag() para que devuelvan un estado que la función llamante comprueba, de modo que drag_remote_file_data() retorna de inmediato cuando la oferta ya ha sido desmontada en lugar de seguir utilizando su puntero local. La macro abrt() se redefine para devolver ese estado exactamente en esas cuatro funciones y se restaura después a su forma original, por lo que ninguna otra ruta de error cambia de comportamiento.

Créditos

  • Gabriel Machado Tavares - descubridor
  • Cristian Fernández Cornejo - analista
  • Xoán M. Otero Jorge - analista
  • Secur0 CNA - coordinador
  • Kovid Goyal - desarrollador de la corrección Origen del descubrimiento: Externo

Registro oficial: CVE-2026-95834

Avisos relacionados

  • CVE-2026-95832 - Severidad crítica
  • CVE-2026-95835 - Severidad media