Summary
A heap out-of-bounds read vulnerability exists in the LibVNCClient UltraZip encoding handler (HandleUltraZipBPP in src/libvncclient/ultra.c). A malicious VNC server can send a crafted FramebufferUpdate message with an attacker-controlled subrectangle count, causing the client to read beyond the bounds of a heap buffer. This leads to information disclosure or denial of service (crash). No authentication is required on the server side (rfbSecTypeNone), and UltraZip encoding is enabled by default in LibVNCClient.
Details
The function HandleUltraZipBPP() (ultra.c, line 122) processes UltraZip-encoded FramebufferUpdate rectangles. For this encoding, the outer rectangle header fields have special semantics — rect.r.x represents the number of sub-rectangles (numCacheRects).
At line 130, numCacheRects is assigned directly from the attacker-controlled rx parameter:
unsigned int numCacheRects = rx;
The subrectangle parsing loop at line 197 iterates numCacheRects times. Each iteration advances the ptr pointer by at least 12 bytes (lines 202–206):
for (i=0; i<numCacheRects; i++)
{
unsigned short sx, sy, sw, sh;
unsigned int se;
memcpy((char *)&sx, ptr, 2); ptr += 2;
memcpy((char *)&sy, ptr, 2); ptr += 2;
memcpy((char *)&sw, ptr, 2); ptr += 2;
memcpy((char *)&sh, ptr, 2); ptr += 2;
memcpy((char *)&se, ptr, 4); ptr += 4;
No bounds check is performed to ensure ptr remains within raw_buffer.
Crucially, the standard bounds check in rfbclient.c is explicitly skipped for UltraZip encoding (line 2250):
if (rect.encoding != rfbEncodingUltraZip)
{
if ((rect.r.x + rect.r.w > client->width) || ...)
This means the attacker-controlled rect.r.x (used as numCacheRects) reaches HandleUltraZipBPP completely unchecked.
Relationship to existing CVEs:
- CVE-2016-9941 added
CheckRect() inside GotBitmap/CopyRectangle, which prevents OOB writes to the framebuffer. However, the ptr advancement via memcpy in the subrect header parsing (lines 202–206) remains unguarded, making the OOB read the remaining exploitable primitive.
- CVE-2016-9942 changed the LZO decompression function to its safe variant but did not modify the subrectangle parsing loop.
PoC
- Set up a malicious VNC server that:
- Advertises RFB protocol version 3.8
- Offers security type
rfbSecTypeNone (type 1, no authentication)
- Sends a
ServerInit message with arbitrary framebuffer dimensions (e.g., 100×100)
- Wait for a LibVNCClient-based viewer to connect
- After connection setup, send a
FramebufferUpdate message containing one rectangle with:
rect.encoding = 0xFFFF0009 (rfbEncodingUltraZip)
rect.r.x = 10000 (interpreted as numCacheRects)
rect.r.y = 100
rect.r.w = 1
rect.r.h = 0
- Follow with an LZO-compressed payload containing a small valid LZO stream (e.g., 120 bytes of decompressed data)
- The client decompresses the data into
raw_buffer (~66 KB), then enters the subrect parsing loop which iterates 10,000 times × 12 bytes = 120,000 bytes, reading ~54 KB beyond the buffer boundary
Impact
Heap out-of-bounds read — A malicious VNC server can cause any VNC client built on LibVNCClient to read up to tens of kilobytes of heap memory beyond the allocated buffer. This can result in:
- Information disclosure: sensitive data from adjacent heap allocations may be leaked
- Denial of service: crash due to reading unmapped memory
Any application using LibVNCClient is affected when connecting to an untrusted VNC server. UltraZip encoding is registered by default in SetFormatAndEncodings() (rfbclient.c, lines 1384–1385).
Summary
A heap out-of-bounds read vulnerability exists in the LibVNCClient UltraZip encoding handler (
HandleUltraZipBPPinsrc/libvncclient/ultra.c). A malicious VNC server can send a crafted FramebufferUpdate message with an attacker-controlled subrectangle count, causing the client to read beyond the bounds of a heap buffer. This leads to information disclosure or denial of service (crash). No authentication is required on the server side (rfbSecTypeNone), and UltraZip encoding is enabled by default in LibVNCClient.Details
The function
HandleUltraZipBPP()(ultra.c, line 122) processes UltraZip-encoded FramebufferUpdate rectangles. For this encoding, the outer rectangle header fields have special semantics —rect.r.xrepresents the number of sub-rectangles (numCacheRects).At line 130,
numCacheRectsis assigned directly from the attacker-controlledrxparameter:The subrectangle parsing loop at line 197 iterates
numCacheRectstimes. Each iteration advances theptrpointer by at least 12 bytes (lines 202–206):No bounds check is performed to ensure
ptrremains withinraw_buffer.Crucially, the standard bounds check in
rfbclient.cis explicitly skipped for UltraZip encoding (line 2250):This means the attacker-controlled
rect.r.x(used asnumCacheRects) reachesHandleUltraZipBPPcompletely unchecked.Relationship to existing CVEs:
CheckRect()insideGotBitmap/CopyRectangle, which prevents OOB writes to the framebuffer. However, theptradvancement viamemcpyin the subrect header parsing (lines 202–206) remains unguarded, making the OOB read the remaining exploitable primitive.PoC
rfbSecTypeNone(type 1, no authentication)ServerInitmessage with arbitrary framebuffer dimensions (e.g., 100×100)FramebufferUpdatemessage containing one rectangle with:rect.encoding=0xFFFF0009(rfbEncodingUltraZip)rect.r.x=10000(interpreted asnumCacheRects)rect.r.y=100rect.r.w=1rect.r.h=0raw_buffer(~66 KB), then enters the subrect parsing loop which iterates 10,000 times × 12 bytes = 120,000 bytes, reading ~54 KB beyond the buffer boundaryImpact
Heap out-of-bounds read — A malicious VNC server can cause any VNC client built on LibVNCClient to read up to tens of kilobytes of heap memory beyond the allocated buffer. This can result in:
Any application using LibVNCClient is affected when connecting to an untrusted VNC server. UltraZip encoding is registered by default in
SetFormatAndEncodings()(rfbclient.c, lines 1384–1385).