TightVNC-Passwörter entschlüsseln…

Letztens hatte ich euch erklärt, dass es möglich ist, die Passwörter zu entschlüsseln, die das Tool TightVNC in der Registry ablegt. Heute möchte ich euch gerne anhand eines Quelltextes zeigen, wie das gemacht werden kann 🙂 . Den Ansporn dazu hat mir Hannes Schurig mit seinem neuem Artikel zu TightVNC gegeben... darin beschreibt er, wie man TightVNC für die Fernwartung mehrerer Rechner nutzen kann - inklusive automatisierter Konfiguration und allem drum und dran.

Leider ist mir bei der ganzen Sache aufgefallen, dass das Problem der Passwort-Wiederherstellung viel zu stiefmütterlich behandelt wurde. Ja, es wurde gesagt, dass die Wiederherstellung möglich ist - allerdings wurde auch darauf verwiesen, dass dies nur mit viel Können und Forscherdrang möglich sei. Um diese Aussage zu widerlegen, habe ich nun kurzerhand das Tool "tvncpass" geschrieben, mit dem man die Passwörter verschlüsseln und auch wieder entschlüsseln kann:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
#include <stdio.h>
#include <string.h>

#include "VncPassCrypt.h"

const char* HEX_CHARS = "0123456789ABCDEF";

#define APP_NAME     "tvncpass"
#define DECODE_PARAM "--decode"
#define ENCODE_PARAM "--encode"

void hexToDecimal(char* hexText, UINT8* decimalText, int decimalLength) {
    int index;

    if (decimalLength > 0) {
        // reset output
        memset(decimalText, 0, decimalLength);

        if ((strlen(hexText) <= decimalLength * 2) && ((strlen(hexText) % 2) == 0)) {
            // only proceed if string length is small enough
            for (index = 0; index < strlen(hexText); index += 2) {
                decimalText[index/2] = ((strchr(HEX_CHARS, hexText[index]) - HEX_CHARS) * strlen(HEX_CHARS)) + (strchr(HEX_CHARS, hexText[index+1]) - HEX_CHARS);
            }
        }
    }
}

void printHelp() {
    printf("Usage: %s (%s|%s) PASSWORD\n", APP_NAME, DECODE_PARAM, ENCODE_PARAM);
    printf("\n");
    printf("The password has to be provided in hexadecimal form.\n");
    printf("In addition to that the password must not be longer\n");
    printf("than 8 bytes and the hexadecimal representation must\n");
    printf("be in all uppercase.\n");
}

void printHex(UINT8* decimalText, int decimalLength) {
    for (int index = 0; index < decimalLength; index++) {
        if (decimalText[index] < strlen(HEX_CHARS)) {
            printf("0%X", decimalText[index]);
        } else {
            printf("%X", decimalText[index]);
        }
    }
    printf("\n");
}

int main(int argc, char** argv) {
    // define variables
    int   index;
    UINT8 input[8];
    bool  isEncode;
    UINT8 output[8];
    char* password;

    if (argc == 3) {
        isEncode = (strcmp(argv[1], ENCODE_PARAM) == 0);
        if (!isEncode) {
            if (strcmp(argv[1], DECODE_PARAM) != 0) {
                // exit if first param does not match
                printHelp();
                return 1;
            }
        }

        // this is our password
        password = argv[2];
        if ((strlen(password) <= 16) && ((strlen(password) % 2) == 0)) {
            // read password as decimal
            hexToDecimal(password, input, sizeof(input)/sizeof(UINT8));

            // encrypt or decrypt
            if (isEncode) {
                VncPassCrypt::getEncryptedPass(output, input);
            } else {
                VncPassCrypt::getPlainPass(output, input);
            }

            // print result as hex
            printHex(output, sizeof(output)/sizeof(UINT8));
        } else {
            // the password must not be longer than 8 bytes
            printHelp();
            return 1;
        }
    } else {
        // exit if wrong number of params
        printHelp();
        return 1;
    }

    return 0;
}

Da der Quelltext ziemlich quick-and-dirty geschrieben wurde, gibt es dieses Mal keine kompilierte Version des ganzen - die müsst ihr euch schon selber erstellen 😛 . Wenn ihr das wirklich getan haben solltet, habt ihr ein Tool, das ihr wie folgt aufrufen könnt:

1
tvncpass (--decode|--encode) PASSWORD

Wenn ihr den "--decode" Modus nutzt, könnt ihr das Passwort entschlüsseln, wenn ihr den "--encode" Modus nutzt, könnt ihr es verschlüsseln. Das Passwort selber muss dabei in Hexadezimalform und in Großbuchstaben übergeben werden - und es darf nicht länger als 16 Hexadezimal-Zeichen lang sein. Um also z.B. das Passwort "test" zu verschlüsseln, muss es als "74657374" an das Programm übergeben werden:

1
tvncpass --encode 74657374

Um daraus nun wieder das ursprüngliche Passwort zu erhalten, müsst ihr das Ergebnis der ersten Operation an das Programm übergeben:

1
tvncpass --decode 2F981DC548E09EC2

Mit diesen ganzen Informationen im Schlepptau kann ich nun endlich die Idee erklären, die ich im Kommentarbereich von Hannes erwähnt hatte. Wir erinnern uns: Bei seiner derzeitigen Implementierung erhalten alle Nutzer-PCs ein und dasselbe Passwort zugewiesen. Wenn nun einer dieser Nutzer sein eigenes Passwort entschlüsselt, kann er damit alle anderen Rechner im Netzwerk fernsteuern.

Dagegen könnte man nun folgendes tun: Man könnte die automatisierte Installation von Hannes nehmen und um einen weiteren Schritt ergänzen. In diesem gibt es einen Server, der zufällig generierte TightVNC-Passwörter erzeugen kann und ein kleines Script auf dem Rechner des Nutzers, der dadurch ein neues Passwort von diesem Server erfragen kann.
Sobald die eigentliche Installation abgeschlossen wurde, könnte dieses Script aufgerufen werden, das ein Passwort vom Server abholt und in die Registry schreibt. Der Server selber könnte sich merken, welche IP (= welcher Nutzer) welches Passwort erhalten hat und dies in einer Tabelle speichern. Das Script selber könnte das erhaltene Passwort in die Registry speichern.
Auf diese Weise hätte jeder Arbeitsplatz ein individuelles Fernwartungspasswort (das der Nutzer zur Not selbstständig erneuern kann), ohne, dass die Administratoren einen großartigen Mehraufwand hätten. 🙂

Sooo... ich hoffe, euch hat dieser neuerliche Exkurs in die Welt von TightVNC gefallen. Mir hat die kleine Fingerübung mal wieder gezeigt, dass C++ eine grausame Sprache ist, die man nur nutzen sollte, wenn man nix besseres zur Hand hat! 😀

Zum Schluss noch etwas Rechtliches:
Der Autor dieses Programms haftet nicht für Schäden an Soft- oder Hardware oder Vermögensschäden, die durch das Benutzen des Programms entstehen, es sei denn, diese beruhen auf einem grob fahrlässigen oder vorsätzlichen Handeln des Autors, seiner Erfüllungsgehilfen oder seiner gesetzlichen Vertreter.
Entschlüsselte Grüße, Kenny

P.S.: Da der Quellcode von TightVNC unter der GPL veröffentlicht wurde, steht natürlich auch der hier gezeigte Quelltext unter der GPL-Lizenz. 🙂

4 Kommentare » Schreibe einen Kommentar

  1. Vielen Dank,
    das Progrämmchen funktioniert prima. In unserem Betrieb ist auf jedem Rechner TightVNC installiert, natürlich mit identischem Passwort...

    Warum benutzt TightVNC an dieser Stelle nicht eine Hash-Funktion, so dass man die Passwörter nicht mehr entschlüsseln kann?

    Beste Grüße

    • Das ist eine gute Frage! Die Antwort darauf findet man in der Beschreibung des RFB-Protokolls. In Kapitel 6.2.2 ist die Art der Authentisierung ("VNC Authentication") beschrieben. Dort steht, dass beim Login der Server eine zufällige 16-Bit-Zahl an den Client schickt. Der Client verschlüsselt das ganze mit dem Nutzerpasswort per DES (verwendet nur 64-Bit-Passwörter, deshalb die Begrenzung auf 8 Zeichen lange Passwörter) und schickt es an den Server. Der muss natürlich prüfen, ob das Ergebnis stimmt und muss daher das Passwort des Nutzers im Klartext kennen.

  2. Hallo,

    ich habe mir mal die Mühe gemacht deinen Code zu testen,
    leider ensteht beim Kompilieren ein Fehler.
    Ein Linker Error. VncPassCrypt::getEncryptedPass und bei VncPassCrypt::getPlain

    Gruß

Schreibe einen Kommentar

Um Ihnen beim weiteren Kommentieren auf dieser Webseite die erneute Eingabe Ihrer Daten zu ersparen, wird beim Absenden Ihres Kommentars ein Cookie an Ihren Browser gesendet und von diesem gespeichert. Mit dem Absenden eines Kommentars auf dieser Webseite stimmen Sie der Speicherung und Übertragung dieses Cookies explizit zu.

Pflichtfelder sind mit * markiert.