El decodificador de TIFF por teselas de Pillow lee más allá del búfer de teselas en copy4 (Unpack.c:1290)
Resumen
El decodificador _decodeTile de libImaging en Pillow, utilizado para decodificar imágenes TIFF divididas en teselas (tiled), asigna un búfer por tesela mediante realloc(state->buffer, TIFFTileSize(tiff)). Tras cada TIFFReadTile, itera tile_y desde 0 hasta current_tile_length e invoca al desempaquetador de filas configurado (en este caso copy4) con el puntero de origen state->buffer + tile_y * row_byte_size. Cuando libtiff devuelve un tile_bytes_size menor que el peor caso row_byte_size * tile_length, el bucle interno de avance y copia sobrepasa la región reasignada y copy4 realiza una operación memcpy de 1024 bytes que lee hasta (tile_length-1) * row_byte_size + row_byte_size bytes de un búfer dimensionado únicamente para tile_bytes_size. El TIFF de prueba de concepto suministrado desencadena una sobrelectura de 1024 bytes exactamente 0 bytes después de una asignación de 196608 bytes. Un atacante que convenza a una aplicación basada en Pillow para decodificar un TIFF manipulado puede provocar el bloqueo del proceso (denegación de servicio) y puede hacer que la memoria adyacente del heap se copie en el búfer de píxeles decodificado.
Detalles
Cuando libtiff indica que el archivo está dividido en teselas, el decodificador TIFF de Pillow toma la ruta _decodeTile. La función:
- Solicita a libtiff
TIFFTileSize(tiff)(los bytes por tesela) yTIFFTileRowSize(tiff)(los bytes por fila tal como los almacena libtiff). - Reasigna
state->buffera exactamentetile_bytes_size. - Para cada origen de tesela
(x, y)y cada plano, llama aTIFFReadTileenstate->buffer, y luego recorre la tesela fila por fila, invocando alImagingShufflerconfigurado (aquícopy4) sobre los bytes de origen correspondientes a cada fila.
La validación que se lleva a cabo antes del bucle es:
if (tile_bytes_size > ((tile_length * state->bits / planes + 7) / 8) * tile_width) {
state->errcode = IMAGING_CODEC_BROKEN;
return -1;
}
Esto rechaza teselas de libtiff de tamaño excesivo, pero no rechaza las de tamaño insuficiente, es decir, el caso en que libtiff devuelve un tile_bytes_size estrictamente menor que row_byte_size * tile_length. Sin embargo, el cuerpo del bucle indexa el búfer asumiendo que tiene una longitud de al menos row_byte_size * tile_length bytes.
Código vulnerable
// src/libImaging/TiffDecode.c:446-490 — _decodeTile
new_data = realloc(state->buffer, state->bytes); // bytes = tile_bytes_size
if (!new_data) { ... }
state->buffer = new_data;
for (y = state->yoff; y < state->ysize; y += tile_length) {
for (plane = 0; plane < planes; plane++) {
ImagingShuffler shuffler = unpackers[plane];
for (x = state->xoff; x < state->xsize; x += tile_width) {
if (TIFFReadTile(tiff, (tdata_t)state->buffer, x, y, 0, plane) == -1) { ... }
current_tile_width = min((INT32)tile_width, state->xsize - x);
current_tile_length = min((INT32)tile_length, state->ysize - y);
for (tile_y = 0; tile_y < current_tile_length; tile_y++) {
shuffler(
(UINT8 *)im->image[tile_y + y] + x * im->pixelsize,
state->buffer + tile_y * row_byte_size, // <-- OOB stride
current_tile_width
);
}
}
}
}
// src/libImaging/Unpack.c:1288-1291 — copy4 (the crash site)
static void
copy4(UINT8 *out, const UINT8 *in, int pixels) {
/* RGBA, CMYK quadruples */
memcpy(out, in, 4 * pixels); // <-- ASAN-detected READ here
}
Los metadatos de la tesela del PoC generan tile_width = tile_length = 256, row_byte_size = 1024 (4 bytes/píxel × 256 píxeles), y tile_bytes_size = 196608 (que equivale a 256·256·3 = almacenamiento de tres canales en bytes). El búfer reasignado tiene 196608 bytes. El desempaquetador itera tile_y desde 0 hasta 255, y para las filas más altas el puntero de origen state->buffer + tile_y * 1024 ya se encuentra en o más allá del desplazamiento 196608; la llamada a memcpy(in=buffer+255·1024, 1024) de la última fila lee 1024 bytes comenzando 260096 - 196608 = 63488 bytes después de la asignación; ASAN marca el primer byte después de la región de heap (READ of size 1024 at … 0 bytes after 196608-byte region).
La discrepancia es real: TIFFTileSize de libtiff devuelve el almacenamiento empaquetado por tesela (que depende del diseño fotométrico/de separación del archivo; legítimamente puede ser inferior a row_byte_size × tile_length), mientras que el bucle interno avanza como si el búfer siempre fuera igual a row_byte_size × tile_length.
Atribución
Este problema fue descubierto por Anthropic mediante el uso de Claude para estudiar la seguridad de proyectos de código abierto, y yo soy de Ada Logics, ayudando a validar los problemas encontrados y reportarlos a los mantenedores. Los datos de este informe han sido revisados manualmente.
Qué hacer
Actualice Pillow a la versión 13.0.0 o posterior.
Software afectado
Haz clic en una tecnología para ver todas sus vulnerabilidades.
| Plataforma | Paquete | Versiones vulnerables | Corregida en |
|---|---|---|---|
| Python (PyPI) | pillow | >= 8.2.0, < 13.0.0 | 13.0.0 |
Referencias
- https://github.com/python-pillow/Pillow/security/advisories/GHSA-462x-pgg6-55fp
- https://nvd.nist.gov/vuln/detail/CVE-2026-112395
- https://github.com/python-pillow/Pillow/commit/76869efdbbf2ad7dccd7536520fb61b7a00ab61e
- https://github.com/python-pillow/Pillow
- https://github.com/python-pillow/Pillow/releases/tag/13.0.0
- Vector CVSS
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:L- Debilidad (CWE)
CWE-122- Plataforma
- Python (PyPI)
- Paquete
pillow- Publicada
- 20.10.2026
- Actualizado
- 20.10.2026 23:28
Fuentes y licencias · Editado o traducido por DockRay; se mantienen los derechos y las licencias de las fuentes.
OSV / GitHub Advisory Database - GHSA-462x-pgg6-55fp - CC BY 4.0
Historial de vulnerabilidades: pillow
Todas las vulnerabilidades publicadas de este paquete (Python (PyPI)), de la más reciente a la más antigua. Muestra si es un fallo aislado o un paquete que hay que vigilar.
- Entradas en total
- 81
- Primera → última
- 04.2014 10.2026
- Sin corrección
- 0
- Explotada en ataques
- 1
Distribución por gravedad
- Crítica 10
- Alta 53
- Media 16
- Baja 1
- Sin evaluar 1
Divulgaciones por año
- 2017: 1
- 2018: 0
- 2019: 1
- 2020: 10
- 2021: 20
- 2022: 8
- 2023: 3
- 2024: 2
- 2025: 1
- 2026: 23
antes: 12
-
El decodificador de TIFF por teselas de Pillow lee más allá del búfer de teselas en copy4 (Unpack.c:1290) esta entrada
Identifica las vulnerabilidades de tu aplicación
Con la integración de DockRay y el inventario de software activado, DockRay compara las versiones notificadas de los componentes con las vulnerabilidades conocidas y avisa de las coincidencias confirmadas. La cobertura depende de la tecnología compatible y de los avisos disponibles.