Tablas XRef evaden límites configurados
GHSA-fjh6-rrhv-4g63 · Asignación de CVE pendiente
Resumen
pdfcpu aplica límites de recursos de referencia cruzada configurados (MaxObjectCount, MaxXRefEntries) al parsear streams XRef (PDF 1.5+), pero el camino de código heredado para tablas XRef tradicionales (PDF 1.4 y anteriores) nunca valida el recuento de objetos de la subsección. Un PDF creado a medida que declara un objCount enorme con entradas coincidentes fuerza al mapa de referencia cruzada a crecer sin límite, agotando la memoria del proceso y crasheando cualquier aplicación anfitriona.
Impacto
- Denegación de Servicio vía agotamiento de memoria — proceso OOM o degradación severa.
- Vector de ataque: red — PDF malicioso vía subida o adjunto.
- Bypass del control intencionado: la configuración de límites de recursos no se aplica silenciosamente en el camino heredado.
- Ámbito: cualquier aplicación que use pdfcpu para procesar PDFs no confiados.
Causa Raíz
En pkg/pdfcpu/read.go, parseXRefTableSubSection() lee el recuento de objetos de la subsección con strconv.Atoi y nunca lo comprueba contra los límites configurados. Cada entrada xref válida se inserta en el mapa de referencia cruzada, que crece sin límite:
// pkg/pdfcpu/read.go:445 — sin comprobación de límite
objCount, err := strconv.Atoi(fields[1])
// read.go:424 — cada entrada hace crecer el mapa
xRefTable.Table[objNr] = &entry
Por contraste, los streams XRef aplican MaxObjectCount = 10M en xRefStreamSize() (model/parse.go) — el camino de tablas tradicionales simplemente carece de la comprobación equivalente.
Prueba de Concepto
Un PDF creado con una tabla XRef tradicional:
xref
0 100000000 ← objCount = 100M, nunca validado
0000000000 65535 f
0000000010 00000 n
... ← el atacante suministra entradas coincidentes
trailer << /Size 100000001 /Root 1 0 R >>
Cada entrada de ~30 bytes crea una entrada de mapa que cuesta ~128 bytes de overhead de mapa Go. 100M entradas → crecimiento de memoria multi-GB → OOM kill. Restricciones mitigantes: el bucle se detiene en la primera línea sin 3 campos, y el atacante debe suministrar suficientes entradas bien formadas para igualar objCount, así que el ataque requiere un archivo grande (escala GB) — pero la amplificación de memoria es aproximadamente 4×.
Cronología de Divulgación
| Fecha | Evento |
|---|---|
| 2026-09-18 | Informe enviado vía GitHub Private Vulnerability Reporting |
| 2026-09-18 | Aceptado por el mantenedor; retitulado "Traditional cross-reference tables bypass configured XRef limits" |
| 2026-09-28 | Aviso publicado — GHSA-fjh6-rrhv-4g63 |
| Corregido en | pdfcpu v0.16.0 |
Corrección
Validar objCount contra MaxObjectCount en el camino de tablas tradicionales, replicando la aplicación de XRef-stream:
// Después (seguro):
objCount, err := strconv.Atoi(fields[1])
if err != nil {
return "", err
}
if objCount > limits.MaxObjectCount {
return "", fmt.Errorf("xref subsection objCount %d exceeds limit %d",
objCount, limits.MaxObjectCount)
}
Corregido en pdfcpu v0.16.0. Aviso publicado 2026-09-28; asignación de CVE pendiente.