TightVNC Viewer Pre-Auth RCE, Fixed in 2.8.90
A malicious VNC server runs code on the TightVNC Viewer before any password prompt; upgrade the Viewer to 2.8.90 and restrict listening mode on TCP 5500.
TL;DR
- The TightVNC Viewer for Windows truncates its pixel buffer size to 32 bits. A malicious VNC server that announces a huge screen can therefore run code on the viewer before any password prompt. SSD Secure Disclosure published the flaw with a working exploit on October 29, 2026.
- Any Viewer before 2.8.89 on the 2.8 line is affected; the researcher demonstrated the exploit on 2.8.88 x64. Help-desk deployments that use Listening Mode expose the viewer on TCP 5500 with no password and no IP restriction, which turns the flaw into pre-auth remote code execution.
- The vendor fixed the viewer in 2.8.89 and 2.8.90, released on October 2 and October 5, 2026, and recommends 2.8.90. No CVE is assigned, no vendor advisory exists, and no exploitation is known; the flaw is not in CISA's Known Exploited Vulnerabilities catalog as of November 2, 2026.
- Upgrade every TightVNC Viewer to 2.8.90. Where the upgrade must wait, turn off Listening Mode or restrict TCP 5500 to trusted hosts, and never point the viewer at an untrusted server. No network restriction covers that second path.
Am I affected?
You are exposed when all of these hold:
- The machine runs the TightVNC Viewer for Windows, version 2.8.88 or any older release of the 2.8 line. The researcher demonstrated the exploit on 2.8.88 x64, and the checks that stop it first appear in 2.8.89.
- One of the two attack paths is reachable. In Listening Mode, started with
tvnviewer.exe -listenor from the tray menu, the viewer binds0.0.0.0:5500and accepts any server that connects, with no password and no IP restriction. Separately, a user who connects the viewer to an attacker-controlled or compromised server is compromised on connect, before the password prompt. - The attacker can reach port 5500, for the listening path, or can run or take over a VNC server the viewer will connect to.
| Branch | Affected versions | Fixed version |
|---|---|---|
| TightVNC 2.8.x | through 2.8.88 | 2.8.90 (2.8.89 closed part of it) |
| TightVNC 2.8.x on Windows XP-era hosts | through 2.8.88 | 2.8.90, the last line that still supports XP |
| TightVNC 4.x | no advisory; released after the fixes | not applicable; current release 4.0.2 (October 28, 2026) |
The 2.8.88 Viewer shipped on June 19, 2026, so on November 2, 2026 the affected code had shipped for 136 days on current installs; older installs carry it longer. The disclosure calls Listening Mode "the usual help-desk deployment", which is what makes the listening path the practical exposure.
Check the installed viewer version and whether it is listening:
(Get-Item "C:\Program Files\TightVNC\tvnviewer.exe").VersionInfo.FileVersion
netstat -ano | findstr :5500
The first command prints the viewer's file version from a default install; adjust the path if TightVNC was installed elsewhere. The second prints any local socket listening on TCP 5500, which is the exposed Listening Mode; an empty result means that path is closed.
How do I fix it?
Upgrade every TightVNC Viewer to 2.8.90:
- Download the 2.8.90 installer for the platform from the vendor's download page and run it; it replaces the viewer in place.
- Confirm the fix with the version check above; the file version must read 2.8.90.
- Restart any viewer that runs in Listening Mode, so the new binary is the one bound to 5500.
The vendor's own release notice for 2.8.90 states that versions 2.8.89 and 2.8.90 "address several security issues in the Viewer" and strongly recommends 2.8.90. For the interval before the upgrade lands, two steps narrow the exposure, and both have limits:
- Turn off Listening Mode on help-desk machines, or restrict TCP 5500 at the firewall to the addresses of trusted VNC servers. The limit is plain: the flaw stays in the viewer, and any untrusted path to 5500 leaves it open.
- Tell operators never to connect the viewer to a server they do not control. No network restriction covers this path, because the connection is outbound.
What is the root cause?
The viewer sizes its pixel buffer from a 32-bit truncated result. Its writes then use offsets derived from the full dimensions the server announced. The code is in FrameBuffer in the viewer's rfb layer, quoted from the 2.8.88 source release.
The size computation multiplies in 64 bits and then discards everything above 32:
int FrameBuffer::getBufferSize() const
{
return (int)((UINT64)m_dimension.area() * m_pixelFormat.bitsPerPixel / 8);
}
bool FrameBuffer::resizeBuffer()
{
if (m_buffer != 0) {
delete []m_buffer;
}
if ((m_buffer = new UINT8[getBufferSize()]) == 0) {
return false;
}
return true;
}
A server announcing a 32768 by 32769 screen at 32 bits per pixel makes the product wrap. The 64-bit product is over 4 GB, the (int) cast keeps 128 KB, and the viewer allocates 128 KB. The stored dimensions keep the full announced 32768 by 32769, so the logical canvas and the allocation disagree by exactly 2^32 bytes (4,294,967,296).
The write offset is then computed in a signed 32-bit int, against the announced width, with no comparison against what was allocated:
void *FrameBuffer::getBufferPtr(int x, int y) const
{
char *ptr = (char *)m_buffer;
ptr += (y * m_dimension.width + x) * getBytesPerPixel();
return (void *)ptr;
}
A large y overflows the product and sign-extends it, so a screen update rectangle can place its pixels at any chosen offset from the buffer, positive or negative. The updates arrive from the server as Raw or Tight rectangles, through RemoteViewerCore::receiveFbUpdateRectangle into the decoders, and the RFB protocol lets the server choose both the geometry and every rectangle.
How is it exploited?
The published exploit needs one connection and no credentials:
- The attacker's RFB server greets the viewer and offers one security type,
None. RFB authentication is one-directional, the server never proves itself to the viewer, and TightVNC's viewer acceptsNone. ServerInitannounces the poisoned geometry, 32768 by 32769 at 32 bits per pixel. The handshake is 45 bytes and allocates the 128 KB buffer the exploit calls FB1.- One Raw rectangle at coordinates
(7492, 32767)lands atFB1 - 0x18AF0, which is them_dataLengthfield of aBufferedOutputStreamobject sitting below the framebuffer. The exploit sets that field to a large value.
s.sendall(b"RFB 003.008\n") # greet as the server
s.sendall(bytes([1, 1])) # offer one security type: None
s.sendall(struct.pack(">I", 0)) # SecurityResult = OK
s.sendall(struct.pack(">HH", 32768, 32769) # ServerInit: the poisoned geometry
+ PIXEL_FORMAT + struct.pack(">I", 3) + b"PWN")
- The next
flush()streams roughly 100 KB of adjacent heap back over the socket. From those bytes the exploit recovers the image base oftvnviewer.exeand the framebuffer address, which defeats address-space randomization. - The exploit writes a fake object and a return-oriented programming chain inside the 128 KB framebuffer, then overwrites the
BufferedOutputStreamvtable pointer. The next virtual call follows the fake object, pivots the stack into the framebuffer, and the chain ends inCreateProcessW.
The exploit works against a fresh, default viewer with ASLR and DEP enabled; control-flow guard is absent from the binary. The disclosure publishes the full exploit script for both attack paths, the listening viewer and the outbound connection, including a relaunch mode that restores the viewer after each run.
How does the attack flow?
The attacker only needs the viewer to accept a session, and the truncated allocation turns that session into control of the viewer's process.
malicious RFB server"] --> B["Viewer accepts,
security type None"] B --> C["ServerInit announces
32768 x 32769 at 32 bpp"] C --> D["Buffer sized in 32 bits,
4 GB canvas on 128 KB,
no bounds check"] D --> E["Rectangle offsets write
outside the allocation"] E --> F["Heap leak, vtable hijack,
ROP into CreateProcessW:
code on the viewer"]
What can an attacker do?
The demonstrated impact is code execution in the viewer's process, on the account running the viewer, at the moment the session opens:
- In Listening Mode, any host that can reach TCP 5500 gets this without a password, an IP allowlist or any interaction from the operator. The disclosure describes this as the usual help-desk deployment, so the exposed population is support tooling, not servers.
- On an outbound connection, the compromise happens on connect, before any password prompt, so server-side VNC passwords do not help against a taken-over server.
- The exploit can hide its payload window with
CREATE_NO_WINDOW, relaunch the viewer after execution, and crash the viewer at will as a denial of service.
The conditions are narrow: the attacker must be the server the viewer talks to, or must reach a listening viewer. A TightVNC server, tvnserver, is not the target here; the direction of the attack is the point, the machine being helped attacking the machine providing help.
Why does the fix work?
The 2.8.89 source adds a new file, viewer-core/ViewerBufferSize.h, with 64-bit size predicates, and validates the server's announcements before any allocation:
inline bool fitsFrameBuffer(int width, int height, int bytesPerPixel)
{
if (width < 0 || height < 0 || bytesPerPixel <= 0) {
return false;
}
const UINT64 alignedWidth = (static_cast<UINT64>(width) + 3) & ~static_cast<UINT64>(3);
const UINT64 alignedHeight = (static_cast<UINT64>(height) + 3) & ~static_cast<UINT64>(3);
return alignedWidth * alignedHeight <= INT_MAX / bytesPerPixel;
}
RemoteViewerCore::setFbProperties, which consumes the ServerInit geometry, now rejects it up front:
if (!ViewerBufferSize::fitsFrameBuffer(width, height, bitsPerPixel / 8)) {
throw Exception(_T("Framebuffer is too large"));
}
The same pattern guards the rich-cursor path, and in 2.8.89 the Tight decoder also rejects oversized JPEG rectangles. In 2.8.90 the Tight decoder adds two more checks: it rejects decompressed data whose size does not match the declared rectangle, and empty compressed blocks. These are the "several security issues in the Viewer" the vendor's release notice names.
The fix is complete for the demonstrated paths. No server announcement can reach the truncated allocation anymore, because the geometry is validated at the protocol boundary before the buffer is sized. The truncating arithmetic itself still sits in FrameBuffer::getBufferSize, unchanged in 2.8.90, so safety now depends on every caller validating first. That is a narrowing to a validated entry path rather than the removal of the bug class.
How do I check if I was compromised?
The disclosure publishes no indicators of compromise. The attacker's traces are on the viewer host, and each check below follows from the published mechanics:
Get-WinEvent -LogName Application | Where-Object { $_.Message -match "tvnviewer" } | Select -First 20
Unexplained viewer crashes in the application log mark the denial-of-service variant. The exploit's payload runs as a child of the viewer, so check process-creation telemetry for any process whose parent is tvnviewer.exe. On Sysmon-enabled hosts, Event ID 1 with that parent is the query.
On listening viewers, review network logs for inbound connections to TCP 5500 followed by an outbound burst from the viewer of roughly 100 KB. That burst is the heap leak the exploit reads back.
If a viewer was exposed and shows any of these signs, treat the operator's account as compromised and rotate what that account could reach.
Timeline
- 2026-06-19: TightVNC 2.8.88, the demonstrated affected version, is released.
- 2026-10-02: TightVNC 2.8.89 ships checks for oversized framebuffers, RichCursor updates and Tight JPEG rectangles; the vendor recommends the upgrade.
- 2026-10-05: TightVNC 2.8.90 ships the remaining viewer fixes, validating decompressed sizes and rejecting empty compressed blocks; the source carries this date.
- 2026-10-29: SSD Secure Disclosure publishes the analysis and the exploit, crediting SeongJoon Cho of SSD Labs Korea, and reports that the vendor never answered the report.
- 2026-11-02: No CVE is assigned, no vendor advisory is published, and no exploitation is known; the flaw is not in CISA's Known Exploited Vulnerabilities catalog.
FAQ
Is the TightVNC server also affected?
No. The target is the Viewer, tvnviewer.exe, and the attack travels from the server to the viewer. A host that only runs the TightVNC server is exposed only to the extent its operators also use the viewer to connect out.
Does a VNC password stop this?
No. The viewer accepts security type None, and RFB authentication never proves the server's identity to the viewer anyway. The outbound attack path completes before any password prompt, so a strong password on the real server does not help once that server is the attacker's.
Has this class hit TightVNC before?
Yes, twice, both times from a malicious server. TightVNC 1.3.10 fixed viewer integer overflows reported by Core Security Technologies, which let a modified VNC server corrupt the viewer's heap. Version 2.8.63 fixed a buffer overrun that let an "evil" server execute code on the viewer machine. The vendor's own release notes record both.
Is TightVNC 4 affected?
No advisory covers the 4.x line. It was released on October 25, 2026, after the 2.8.89 and 2.8.90 fixes. The 4.0 announcement describes a new security model, not this viewer bug. Confirm with the vendor if you run 4.x; on 2.8.x, take 2.8.90 regardless.
Why is there no CVE?
The vendor publishes no advisory, and the researcher's report went unanswered, so no CVE was assigned as of November 2, 2026. Dependency and vulnerability scanners therefore cannot flag the affected range; the version check in "Am I affected?" is the only reliable detection.
Where do I get the fixed viewer?
From the vendor's download page, which offers the 2.8.90 installers alongside the 4.0.2 line. The 2.8.90 source is published under the GPL at the same location, which is where this post's fix analysis comes from.
Find hosts exposing a listening TightVNC viewer with Ostorlab
Ostorlab's attack surface discovery inventories your internet-facing hosts, so you can find which support machines answer on TCP 5500 with a listening viewer and upgrade those first.
References
- SSD Secure Disclosure, "TightVNC Viewer Pre-auth RCE", the analysis and exploit, credit SeongJoon Cho of SSD Labs Korea: https://ssd-disclosure.com/tightvnc-viewer-pre-auth-rce/ (published October 29, 2026; checked November 2, 2026)
- TightVNC release notes, Viewer fixes in 2.8.89 and 2.8.90: https://www.tightvnc.com/whatsnew.php (checked November 2, 2026)
- TightVNC news, the October 2 and October 5 release announcements and the June 19 2.8.88 release: https://www.tightvnc.com/news.php (checked November 2, 2026)
- TightVNC download page, 2.8.90 and 4.x installers and GPL source archives: https://www.tightvnc.com/download/ (checked November 2, 2026)
- TightVNC 2.8.88 source release, the vulnerable FrameBuffer: https://www.tightvnc.com/download/2.8.88/tightvnc-2.8.88-src-gpl.zip (checked November 2, 2026)
- TightVNC 2.8.90 source release, ViewerBufferSize.h and the validation call sites: https://www.tightvnc.com/download/2.8.90/tightvnc-2.8.90-src-gpl.zip (checked November 2, 2026)
- CyberSecurityNews, "TightVNC Pre-Auth RCE Vulnerability Lets Hackers Execute Code on Windows PCs, PoC Released": https://cybersecuritynews.com/tightvnc-pre-auth-rce-vulnerability/ (published November 2, 2026; checked November 2, 2026)
- CISA Known Exploited Vulnerabilities catalog, checked for TightVNC entries: https://www.cisa.gov/known-exploited-vulnerabilities-catalog (checked November 2, 2026)