TALOS 2026 2422
A memory corruption vulnerability exists in the vtkBMPReader ExecuteDataWithInformation functionality of vtk (version(s): 9.6.1). A specially crafted .bmp file 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.
vtkBMPReader parses the Windows Bitmap (BMP) image format. The reader parses a malformed BMP sample. ASan reports:
A heap-buffer-overflow WRITE of 2 bytes at LINE_428 inside vtkBMPReaderUpdate2 () , called from ExecuteDataWithInformation() at LINE_476 . The write lands at address 0x502000007ff2 , exactly 0 bytes after the end of a 2-byte allocated region. The error message on the first line — emitted before the crash — indicates that the reader recognized the bit depth as unsupported.
The crash-site code in vtkBMPReaderUpdate2() :
self->GetDepth() returns the bit depth read from the BMP header — a member variable this->Depth . The first two branches handle depth 8. The else branch at LINE_425 is a catch-all: any depth that is not 8 enters this branch, which writes 3 components per pixel into the output buffer ( outPtr0[0] , outPtr0[1] , outPtr0[2] at LINE_427 – LINE_429 ). outPtr0 is a pointer into outPtr , which was passed from the caller.
One level up in the call stack (frame #1), ExecuteDataWithInformation() allocates the output buffer and dispatches to vtkBMPReaderUpdate2() :
AllocateOutputData() at LINE_453 sizes the output buffer based on the scalar component count — a value configured during the information phase. GetScalarPointer() at LINE_473 returns a pointer to that buffer. At LINE_476 , the function dispatches into vtkBMPReaderUpdate2() with this (which carries this->Depth ) and outPtr . No validation of this->Depth occurs between LINE_451 and LINE_476 .
The buffer is sized by the scalar component count. That count is set in ExecuteInformation() , which also reads the bit depth from the file header:
this->Depth is a 2-byte integer read from the BMP DIB header at LINE_138 — its value is attacker-controlled. At LINE_145 , the function checks whether the depth is 8 or 24. For any other value, it emits an error message at LINE_147 and returns at LINE_148 .
The code at LINE_218 – LINE_226 configures the scalar component count: 1 for 8-bit grayscale, 3 for 24-bit RGB. The early return at LINE_148 exits the function before this code is reached. The scalar component count is therefore left at whatever default the reader object holds — typically 1.
The pipeline allocates the output buffer based on that component count. With 1 component, the buffer has room for one value per pixel. But the else branch in vtkBMPReaderUpdate2() writes 3 values per pixel — the buffer is too small by a factor of 3.
VTK’s pipeline dispatches REQUEST_INFORMATION and REQUEST_DATA as separate requests. ExecuteInformation() is called during the information phase; ExecuteDataWithInformation() during the data phase. The early return at LINE_148 exits the information phase — but does not prevent the data phase from running. The parent class vtkImageReader2::RequestInformation() calls this->ExecuteInformation() and then checks this->GetErrorCode() to decide whether to report failure. vtkBMPReader::ExecuteInformation() never calls this->SetErrorCode() before returning at LINE_148 — GetErrorCode() returns NoError , RequestInformation() returns success, and the pipeline proceeds to the data phase.
The data phase runs with this->Depth still holding the attacker-controlled value (unchanged since LINE_138 ), while the buffer was sized for a component count that was never configured for that depth. The else branch at LINE_425 writes 3 components into a 1-component buffer, producing the heap overflow.
The attacker controls the content written: the values at LINE_427 – LINE_429 come from inPtr , which points to raw pixel data from the BMP file. The attacker also controls which branch is taken: any bit depth value other than 8 enters the else branch at LINE_425 , which writes 3 components per pixel into a buffer sized for 1.
The observed crash is an out-of-bounds write into heap memory. Because the write lands immediately past the allocated buffer boundary, it corrupts adjacent heap metadata or application objects. On non-instrumented builds this corruption is silent — no error is raised and the process continues with a compromised heap state.
Vendor Response (CVE-2026-8824)
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.
