
Informe de una vulnerabilidad de desbordamiento de montón en el programa split de GNU coreutils. CVE-2024-0684
Una vulnerabilidad en el programa split de GNU coreutils permite un desbordamiento del búfer del heap con datos controlados por el usuario.
Fue introducida en 40bf1591bb4362fa91e501bcec7c2029c5f65a43 el 2023-03-04. Se ha publicado una corrección con c4c5ed8f4e9cd55a12966d4f520e3a13101637d9 el 2024-01-17.
Versiones afectadas: GNU coreutils v9.4; v9.3; v9.2
Prueba de concepto:
El archivo de ejemplo split_me en este repositorio se puede utilizar para provocar un fallo en las versiones afectadas.
split -C 1024 ./split_me
Esto hará que split falle con una violación de segmento (SIGABRT).
Descubrí esta vulnerabilidad mientras intentaba automatizar la extracción de datos de sistemas aislados (air-gapped) utilizando códigos QR.
Los códigos QR generados con qrencode tienen una capacidad de ~4000 caracteres, por lo que requería un uso intensivo de split.
En un caso de prueba específico, split falló con una violación de segmento.
Dado que GNU coreutils es de código abierto, podemos usar el código fuente para identificar el error, en lugar de tener que aplicar ingeniería inversa a un binario. En proyectos de código abierto, debes ser lo más específico posible en tu informe de error, idealmente proporcionando el commit y la línea exactos que introdujeron el error, así como una corrección propuesta. Esto permite a los mantenedores verificar tu informe rápidamente y acorta el tiempo de respuesta.
Al verificar el error en diferentes sistemas,
noté que el fallo solo ocurría en versiones relativamente recientes de split.
Si tienes un commit bueno y uno malo,
eso te permite realizar una búsqueda binaria en el historial de commits,
para encontrar el commit que realmente introdujo el error.
Git proporciona una herramienta específica para este caso de uso: git bisect.
Sugerirá automáticamente commits para probar y te permitirá marcarlos como buenos o malos.
Eventualmente, terminas con el commit que introdujo el error; en nuestro caso:
commit 40bf1591bb4362fa91e501bcec7c2029c5f65a43
Author: Paul Eggert <[email protected]>
Date: Sat Mar 4 11:42:16 2023 -0800
split: prefer signed integers to size_t
This allows for better runtime checking with gcc
-fsanitize=undefined.
* src/split.c: Include idx.h.
(open_pipes_alloc, n_open_pipes, suffix_length)
(set_suffix_length, input_file_size, sufindex, outbase_length)
(outfile_length, addsuf_length, create, cwrite, bytes_split)
(lines_split, line_bytes_split, lines_chunk_split)
(bytes_chunk_extract, ofile_open, lines_rr, main):
Prefer signed integers (typically idx_t) to size_t.
src/split.c | 105 ++++++++++++++++++++++++++++++------------------------------
1 file changed, 52 insertions(+), 53 deletions(-)
Luego podemos compilar el programa (idealmente con un sanitizador de direcciones),
para encontrar la línea exacta donde falla.
Con solo ~50 líneas para revisar,
se vuelve fácil identificar el error.
En nuestro caso, el fallo ocurrió en una llamada a memcpy() con índices incorrectos.
Y de hecho, si revisamos el área alrededor de la llamada a memcpy(),
encontramos un diff que cambia los cálculos de índices justo antes:
@@ -816,15 +820,10 @@
/* Update hold if needed. */
if ((eoc && split_rest) || (!eoc && n_left))
{
- size_t n_buf = eoc ? split_rest : n_left;
+ idx_t n_buf = eoc ? split_rest : n_left;
if (hold_size - n_hold < n_buf)
- {
- if (hold_size <= SIZE_MAX - bufsize)
- hold_size += bufsize;
- else
- xalloc_die ();
- hold = xrealloc (hold, hold_size);
- }
+ hold = xpalloc (hold, &hold_size, n_buf - (hold_size - n_hold),
+ -1, sizeof *hold);
memcpy (hold + n_hold, sob, n_buf);
n_hold += n_buf;
n_left -= n_buf;
Si revertimos estos cambios y recompilamos,
split procesa todos nuestros casos de prueba sin errores.
Solo queda revisar la lógica para verificar el error y desarrollar una corrección.