Mit unsicherem Code ins Weltall

Hallo zusammen,

zur Abwechslung mal was über Krypto. Ein Bekannter hat mir einen Hinweis gegeben, dass in einer Stargate SG1 Folge (S7E09 „Avenger 2.0“) auf einer Tafel etwas geschrieben stand, was recht „kryptisch“ aussieht. Hier ein Bild der Szene (ca. Minute 12):

Bildzitat aus Stargate SG1 Episode Staffel 7 Folge 9 "Avenger 2.0"

Bildzitat aus Stargate SG1 Episode Staffel 7 Folge 9 „Avenger 2.0“

Auf den ersten Blick vermutet man Java, was natürlich recht amüsant wäre: Ob (damals Sun) Oracle wohl eine JVM für Stargates (bzw. Wahlgeräte) anbietet? Tatsächlich ist es jedoch C#, was es nicht weniger amüsant macht. Da es eine über 10 Jahre alte Episode ist, ist dies natürlich auch anderen schon aufgefallen. Es handelt sich bei diesem Quelltext um eine etwas intensiver kommentierte Version einer Klasse zur Entschlüsselung von Dateien auf dem Dateisystem, die man hier finden kann. Analog gibt es natürlich auch eine Version zur Verschlüsselung.

Es ist natürlich so, dass die „Macher“ der Serie nur etwas kryptisch aussehenden Programmtext präsentieren wollten und sind dabei über diese Zeilen gestolpert. Aber aus Neugier habe ich mir das Programm etwas näher angesehen. Es besteht aus einer Hilfsklasse StoreCryptoStream, aus einer Klassenmethode zur Generierung des Schlüsselmaterials (GenerateKey) und einer weiteren Klassenmethode zur Vorbereitung und Durchführung der Entschlüsselung (DecryptData).

In der DecryptData-Methode werden Streams eingerichtet und die Entschlüsselung durchgeführt. Aufmerksam wurde ich, als ich mir die GenerateKey-Methode genauer angesehen habe, welche aus einer Passphrase (String) das zur Verschlüsselung (DES) passende Schlüsselmaterial generiert:

          symKey = new byte[8];
          symIV = new byte[8];

          SHA1_CSP sha = new SHA1_CSP();
          sha.Write(bt)
          sha.CloseStream();
          for (int i = 0; i < 8; i++) {
              symKey[i] = sha.Hash[i];
          }
          for (int i = 8; i < 16; i++) {
              symIV[i - 8] = sha.Hash[i];
          }

Das fand ich recht interessant: Hier werden, aus dem Hashwert (SHA1) der Passphrase (String), Schlüssel und Initialisierungsvektor abgeleitet. Da wir bei DES (DecryptData-Methode) eine 64 Bit Schlüssellänge (wovon 56 effektiv sind) und 64 Bit Blocklänge haben, brauchen wir also 128 Bit die uns durch den 160 Bit SHA1 zur Verfügung stehen. Ein Hashverfahren zu verwenden um aus einer Passphrase Schlüsselmaterial zu gewinnen ist ja nicht neu: Dies findet auch bei PBKDF2 statt. Es wird zur Erhöhung der Brute-Force-Resistenz eingesetzt, da die Berechnung eine gewisse Zeit in Anspruch nimmt und nicht abgekürzt werden kann. Dabei werden jedoch sehr viel mehr Iterationen durchgeführt (z.B. 100 000) und nicht nur eine.

Die meisten symmetrischen Blockchiffren benötigen keinen Initialisierungsvektor (IV): so auch der DES nicht. Da allerdings regelmäßig keine blockweise Verschlüsselung sondern ein Betriebsmodus zum Einsatz kommt, hat er einen festen Platz in den Programmbibliotheken. Normalerweise wird der IV vor der Verschlüsselung zufällig generiert und vor/nach der Übermittlung als Klartext mitgesendet. Der Empfänger kann mittels geheimen Schlüssels und mitgesendeten IVs das Chiffrat entschlüsseln. Die Generierung eines IVs aus der Passphrase hat den enormen praktischen Vorteil, dass diese dem verschlüsselten Ciphertext nicht beigefügt sein muss. Der Nachteil ist, dass der IV dann nicht mehr zufällig generiert wird sondern deterministisch vom Schlüssel bzw. der Passphrase abhängt.

So was Ähnliches wird zwar auch im SSH Protokoll durchgeführt. So wird dort unter Einsatz einer Hashfunktion und unterschiedlichen Salt-Werten mehrere Schlüssel und IVs generiert. Allerdings wird hierbei schon zu einem frühen Zeitpunkt der „Zufall“ eingebracht und genau diese Zufälligkeit fehlt beim gezeigten Programm vollständig.

Ist das jetzt gut oder schlecht? Ich habe dazu recherchiert und mir meine eigenen Gedanken gemacht und die Antwort ist: Es kommt darauf an, ist aber fast immer schlecht.

Nehmen wir z.B. Stromchiffren (wie, mittlerweile unsicher, RC4). Hier würde, bei gleichem Passwort, immer der gleiche IV und damit der gleiche Schlüsselstrom generiert werden. Sofern zwei verschlüsselte Texte mit dem gleichen Schlüsselstrom verschlüsselt sind, könnten diese gegenseitig ge-xor-t und damit der Schlüsselstrom herausgerechnet werden. Ebenso bei bei Blockchiffren mit dem entsprechenden Betriebsmodus wie dem Cipher Feedback Mode (CFB) oder dem Output Feedback Mode (OFB): Da hier aus einer Blockchiffre eine Stromchiffre konstruiert wird, haben wir exakt das gleiche Problem. Bei diesen Verfahren hängt die Sicherheit sehr stark an der Zufälligkeit des IVs, welcher so jedoch nicht mehr zufällig ist.

Wie ist es bei einer Blockchiffre im ECB und CBC Modus? Beim ECB-Modus sind wir schmerzfrei, denn dort wird der IV nicht verwendet. Müssen dabei jedoch die allgemeine Unsicherheit des ECBs hinnehmen. Beim CBC-Modus (den ich schonmal erklärt habe) haben wir zunächst auch kein Problem. Allerdings muss man hierbei gut aufpassen: Verschlüsselungsbibliotheken werden beim CBC-Modus den IV ans Ende des Chiffretextes setzen, da dieser üblicherweise zufällig generiert und zur Entschlüsselung gebraucht wird. Das bedeutet aber, dass wir dem verschlüsselten Text ein (Teil)Hash des Passwortes mitsenden! Möglicherweise kann genau dieser Teilhash dann in einer Rainbowtable für SHA1 gefunden und entschlüsselt werden. Ebenso erleichtern wir dem Angreifer die vollständige Schlüsselsuche beträchtlich. Allgemein wird der IV als „öffentlich“ angesehen und wird dementsprechend ungeschützt sein. Eine Klassifizierung, welcher ein einfacher SHA1 Hash der Passphrase nicht haben sollte.

Zusammenfassend sollte man den IV zufällig generieren und auch genau so verwenden. Nur wenn man ganz genau weiß was man tut, kann man von dieser Regel abweichen.

Wie ist es nun bei diesem Programm? Das ist leider nicht ganz klar, denn der Programmtext lässt sich (zumindest unter Visual Studio) nicht mehr übersetzen: in über 10 Jahren hat sich C# diesbezüglich weiterentwickelt. Man könnte das Programm zwar durch Anpassungen wieder lauffähig bekommen, würde jedoch damit ggf. auch die Funktionsweise beeinflussen. Es ist zu vermuten, dass hier der ECB-Modus zum Einsatz kam und so der IV keine Rolle spielt. Wenn der CBC eingesetzt wurde, müsste man prüfen, ob dem Ciphertext der IV beigelegt war. So oder so war jedoch schon damals (und ist heute weiterhin) von der Nutzung dieses Quelltextes (bzw. einer lauffähigen Variante) grundsätzlich abzuraten.

Dies könnte auch der Grund sein, weshalb der in dieser Episode programmierte „Avenger“ (Computervirus) so unsicher war ;-).

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht.