TALOS 2026 2421
A memory corruption vulnerability exists in the vtkDEMReader ReadProfiles functionality of vtk (version(s): 9.6.1). A specially crafted USGS DEM can lead to a buffer overflow. An attacker can provide a malicious file to trigger this vulnerability.
Confirmed Vulnerable Versions
The versions below were either tested or verified to be vulnerable by Talos or confirmed to be vulnerable by the vendor.
vtk (version(s): 9.6.1)
vtk -
7.5 - CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H
VTK, or the Visualization Toolkit, is an open-source software system for 3D computer graphics, image processing, and scientific visualization. It provides a large collection of algorithms and data structures for rendering, volume visualization, geometric processing, and interaction.
vtkDEMReader parses the USGS Digital Elevation Model format — a fixed-width ASCII format. A Type A record (1024 bytes) describes grid geometry (ground coordinates, spatial resolution, row and column counts). B-records follow, each containing a profile: a 24-byte prefix of four 6-digit integers ( profileId[0] , profileId[1] , profileSize[0] , profileSize[1] ) and then elevation samples.
The reader parses a malformed DEM sample without error. ASan reports:
The crash is a 4-byte heap write at LINE_440 inside ReadProfiles() , called from RequestData() at LINE_146 . The write lands at address 0x7f23592b6a14 , exactly 0 bytes after the end of the allocated region — a right-edge overrun.
The crash-site code in ReadProfiles() :
At LINE_395 , four integers are read from the B-record prefix. profileId[0] and profileSize[0] are attacker-controlled values parsed from the file. At LINE_416 , rowId is derived from profileId[0] . At LINE_418 , lastRow is computed from rowId + profileSize[0] - 1 . The loop at LINE_429 iterates row from rowId to lastRow inclusive, and each iteration writes a float elevation value into the output buffer at LINE_440 using row * numberOfColumns as part of the offset.
outPtr points to the scalar buffer of the output vtkImageData . The buffer is allocated by RequestData() (frame #1 in the crash stack). At LINE_124 , RequestData() allocates scalars based on the grid extent published during the information phase:
AllocateScalars() at LINE_124 sizes the buffer from the extent — which encodes NumberOfRows × NumberOfColumns . At LINE_146 , RequestData() calls ReadProfiles(output) , passing the allocated vtkImageData . The buffer size is fixed at this point.
The extent itself is computed during the information phase. RequestInformation() calls ReadTypeARecord() at LINE_96 to parse the Type A header, then derives extent, origin, and spacing at LINE_99 :
RequestInformation() returns 1 unconditionally at LINE_111 . The return value of ReadTypeARecord() at LINE_96 is not checked. The pipeline therefore always proceeds to the data phase, regardless of whether the header was parsed successfully or produced consistent values.
The buffer is sized from the Type A record (grid dimensions), but the write loop in ReadProfiles() is driven by B-record fields ( profileId[0] , profileSize[0] ). These two sets of values are parsed independently from different parts of the file, and no validation enforces consistency between them. Specifically, lastRow computed at LINE_418 must satisfy lastRow < NumberOfRows for the write at LINE_440 to remain within bounds. No such check exists between LINE_395 (where the B-record fields are parsed) and LINE_429 (where the loop begins).
Because profileId[0] and profileSize[0] are read directly from the file, an attacker controls: - the write offset — rowId and lastRow determine where in the buffer the writes land - the write count — profileSize[0] determines how many iterations the loop executes - the written content — elevation at LINE_439 is an attacker-controlled integer
The buffer size is also indirectly attacker-controlled through the Type A record’s ground coordinates and spatial resolution. A crafted DEM file that declares a small grid in the Type A record but provides a B-record profile spanning more rows than the grid contains causes ReadProfiles() to write past the end of the allocated buffer.
On non-instrumented builds, the heap corruption is silent — no error is raised and the process continues with a compromised heap state.
Vendor Response (CVE-2026-8822)
Vendor Link:
2026-06-23 - Vendor Notification 2026-06-25 - Vendor Acknowledged 2026-06-30 - 7-Day 2026-07-02 - Follow-Up Sent 2026-07-07 - Vendor Replied, Dev Team Working 2026-08-07 - 45-Day 2026-08-22 - 60-Day 2026-09-15 - Follow-Up Sent 2026-09-21 - 90-Day 2026-09-23 - Vendor Replied, Dev Team Working 2026-10-12 - Advised Vendor Cisco Will Publish And Close Out
Emmanuel Tacheau of Cisco Talos
The full story
This article is one source in a clustered incident — the cluster page carries the summary, timeline and every other outlet covering it.
