Disgu(i)sting

Posted by eddy14 - 18/10/10 at 10:10 pm

Ja, ich lebe noch.

Der letzte Blogeintrag ist fast schon ein viertel Jahr her. Ich hatte leider während der Semesterferien absolut keine Zeit für meine Hobbys. Ich musste zwangsweise etwas Energie in mein Real-Life investieren, und wer will das schon, richtig? Mir hat irgendwie auch die Motivation gefehlt irgendein Verschlüsselungssystem zu erforschen.

Gehen wir einen Tage zurück: Ich hatte keinen Zugang zum Internet und so langsam sind mir die Bücher ausgegangen, und so musste ich mir anders behelfen. Klar, ich könnte rausgehen in die Natur (haha!), aber ich hatte noch in meiner VirtualBox eine Applikation die darauf gewartet hat analysiert zu werden. Also wurde ich rückfällig, und saß dann gestern Abend wieder vor dem Rechner, um etwas zu analysieren. Das heutige Target nennt sich “Disguise” (na, war der Titel von dem Blogpost nicht einfallsreich?).

Diesmal war es etwas anders. Dieses Programm setzt zwar wieder auf Security-by-obscurity, aber einen Unterschied zu meinen vorherigen Targets hat es: es ist nicht direkt gebrochen, allein wenn man den Schlüssel und den Algorithmus gefunden hat.

In diesem Fall musste ich (un?)glücklicherweise tatsächlich eine Kryptoanalyse durchführen. Es ist meine erste echte Kryptoanalyse von einem unbekannten Verfahren gewesen, und wie das Leben so spielt, war es garnicht so schwer. Ich bin quasi in das Thema hineingerutscht.

Nun, was ist denn dieses Disguise? Auf der Webseite heißt es:

I share with you, with Disguise, a SECURE dual-keys proprietary multi-stage encryption algorithm to protect your PRECIOUS DATA.

(die Hervorhebungen sind originalgetreu übernommen)

Na sieh sich das mal einer an. Klingt doch super! Wäre da nur nicht dieses kleine winzige Wort “proprietary”, gäbe es keinen Grund für mich dieses Programm zu analysieren. Denn sonst hätte ich gedacht, dass das Verfahren etwas wie AES ist. Aber so weiß ich, dass der Programmierer vermutlich einen eigenen Verschlüsselungsalgorithmus entworfen hat. Und wer schonmal einen Kryptoexperten erlebt hat, der euch förmlich anbettelt, niemals einen eigenen Verschlüsselungsalgorithmus zu entwerfen, der wird spätestens in diesem Blogeintrag erfahren wieso.

Mittlerweile scheint es mir so, als könne man schlechte Verschlüsselung bereits an der GUI des Programmes entlarven. Vielleicht erinnert sich noch jemand an das hier. Jedenfalls, ist dieses Programm genauso hässlich:

Ekelhaft. Na vielleicht tut es ja seine Arbeit gut. Nämlich: Daten sicher verschlüsseln (hey, ihr seid auf meinem Blog, was glaubt ihr?)

Vorerst machen wir eine Blackbox Analyse. Ich werde mir also vorerst nicht den Programmcode anschauen. Ich werde eine Datei einspielen, und mir das Resultat anschauen.

Ich habe also testweise eine Datei verschlüsseln wollen. Und in dem Moment hatte mich das Programm nach zwei Daten gefragt. Es wollte von mir zwei so genannte “Signaturen” haben. Im Prinzip meint der damit nur ein Passwort. Ich sollte also zwei Passwörter eingeben. Allerdings durften diese nicht länger als 8 Zeichen sein. 8 Bytes? Das sind nur 64 Bit für einen Schlüssel. Allerdings will er zwei mal 8 Bytes. Wer weiß, was er mit diesen Passwörtern genau tut ?!

Ich werde im Laufe dieses Blogeintrages folgenden Plaintext als Eingabedatei “god.txt” verwenden (damit hier einige nicht rumheulen: es ist tatsächlich die erste txt Datei auf meinem Rechner, die mir unter die Finger kam; es hat keine weitere Bedeutung):

Is God willing to prevent evil, but not able? Then he is not omnipotent.
Is he able, but not willing? Then he is malevolent.
Is he both able and willing? Then whence cometh evil?
Is he neither able nor willing? Then why call him God?

Nach der Verschlüsselung hatte ich eine Datei “goddis.txt”, mit dem Inhalt:

Na das sieht doch ziemlich verschlüsselt aus. Was mir als erstes aufgefallen ist: die Ausgabedatei ist um 33 Bytes größer als das Original. Was hat der Algo da bloß getan? Beim Versuch andere Dateien zu verschlüsseln, ist jedesmal die Ausgabedatei um 33 Bytes größer.

Ich mache jetzt etwas ganz simples. Ich werde die Ausgabe Datei goddis.txt mal nach der statistischen Häufigkeit der Bytes untersuchen. Da bietet mir mein Hexeditor HxD eine passende Funktionalität. Die Ausgabe sieht so aus:

Es gibt da nicht viel zu sehen. Man erkennt bloß, dass einige Bytes garnicht vorkommen, wohingegen die letzten Bytes (0xF4 bis 0xF7) verglichen mit den anderen Bytes, ziemlich oft in der verschlüsselten Datei auftauchen. 0xF7 taucht sogar ganze 17 mal auf (das sind 6,32% der ganzen Datei).

Schauen wir uns das Resultat der Verschlüsselung von standardisierten Verschlüsselungsalgorithmen an. Hier in dem Fall ist es AES:

Die statistische Verteilung der Bytes sieht schon ziemlich zufällig aus. Das Byte 0×35 kommt hier am häufigsten vor, mit genau 6 Auftritten (das sind 2,49% der Datei). Zum Vergleich schauen wir uns noch eine rein zufällige generierte Datei an:

Es gibt in diesem Fall mehrere Bytes die das Maximum darstellen. Sie tauchen genau 4 mal auf, und machen damit 1,66% der Datei aus.

Verglichen damit steht Disguise sehr doof da. Es gibt einem das Gefühl, dass die Ausgabe nicht wirklich durchgewürfelt ist; und vielleicht haben wir sogar eine Möglichkeit, den Algorithmus zu brechen.

Genug Blackbox Analyse. Wir öffnen das Programm mit unserem Lieblingsdisassembler/debugger und schauen uns an, was das Programm tut. Da ich in diesem Beitrag mehr auf die Analyse des Verfahrens eingehen möchte, und nicht schon wieder auf das Reverse Engineering (dazu schaut ihr lieber hier nach), werde ich kurz beschreiben was ich alles über den Algorithmus herausgefunden habe:

Es gibt eine erste Runde die ich “Phase 1″ getauft habe. In dieser Phase wird ein “Pseudo”-Ciphertext generiert, der einfach wieder zurück zum Ursprungszustand versetzt werden kann. Danach folgt die Phase 2, wo dann die zweite Signatur angewendet wird, um es zu verschlüsseln. Und dann haben wir unsere verschlüsselte Datei.

Ein etwas tieferer Einblick: Zuerst wird aus der Signatur 1 (hier der Einfachheit zur Liebe: “sign123″) etwas generiert, was ich sig1c nenne. Dabei wird folgendermaßen vorgegangen: Wir lesen das aktuelle Byte aus (hier “s” = 0×73). Dieser wird reversed zu 0×37. Nun führt man eine OR-Verknüpfung mit dem Wert 0×08 durch (= 0x3F) und negiert anschließend das Ganze (= 0xC0). Das macht man mit allen Bytes der Signatur. Alles hinter der Signatur ist 0×00. Diese pseudo-Verschlüsselung wird genau 31 mal angewandt. Nun wird ein 0×00 Byte angeschlossen und dahinter wird ein konstanter Wert “03″ hinzugefügt. Das sind insgesamt 33 Bytes, welcher der Verschlüsselten Datei vorne angehangen werden (das erklärt schonmal dieses Mysterium!).

Aber mooooment. Ich sagte gerade, dass hinter dem sign123 nur noch 0×00 Bytes folgen. Wenn man genau dieses Verfahren auf 0×00 anwendet, dann kommt stets 0xF7 heraus. Das könnte eventuell die statistische Häufigkeit der 0xF7 in der Ausgabe Datei erklären.

Das erzeugte sig1c sieht nun so aus: C0 61 81 11 E4 D4 C4 F7 F7 F7 F7 F7 F7 F7 F7 F7 F7 F7 F7 F7 F7 F7 F7 F7 F7 F7 F7 F7 F7 F7 F7 00 03

Das ist der Header, der jeder verschlüsselten Datei hinzugefügt wird (wobei die ersten 7 Bytes variieren, aufgrund der ersten Signatur; der Rest ist stets gleich!).

Der eine oder andere fragt sich nun: Wieso ist die Signatur nur 7 Bytes lang, obwohl das Programm 8 Zeichen haben will? Ich weiß nicht ob das so gewollt ist, aber das Programm liest nur 7 Bytes, und reserviert das 8te für das 0×00 Byte, um den String abzuschließen. Obwohl das total überflüssig ist. Wie wir nachher sehen werden, hat dieser kleine “Bug” mir Tür und Tor geöffnet.

Dieses sig1c-Verfahren wird auf die ganze Plaintext-Datei angewandt. Nun haben wir diesen Pseudo-Ciphertext, der wieder zurück zum Urprungszustand versetzt werden kann (es ist reversible/umkehrbar).

Auf diesen Zustand wird nun die Phase 2 angewandt. Dieser geht wie folgt:

Die Datei wird in 8-Byte Schritten eingelesen. Nun wird mit jedem Byte dessen mit dem aktuellen Byte der zweiten Signatur XOR verknüpft. (Bedenke: das letzte Byte der zweiten Signatur ist auch 0×00! Einfachheitshalber ist meine zweite Signatur “ABCDEFG”).

Beispiel von oben: Das erste Byte war 0xC0. Dieser wird mit 0×41 (“A”) geXORed. Es ergibt 0×81. Das Ergebnis des ersten Durchlaufes ist: 81 23 C2 55 A1 92 83 F7.

Nach jedem Durchlauf der 8 Byte, wird ein “shuffle” durchgeführt. Die Signatur2 wird verwürfelt. Allerdings hat sich der Programmierer hier sehr dumm angestellt (aber er hat anscheinend Ideen in die richtige Richtung gehabt!): Die Verwürfelung der Signatur ist nicht ständig das selbe. Es gibt 5 verschiedene Funktionen. Ein Counter wird jeweils modulo 5 geteilt, und jenachdem wird die jeweilige Funktion aufgerufen. Der Counter wird anfangs auf das 4te Byte des sig1c gesetzt (hier: 0×11 = 17).

17 modulo 5 ergibt 2. Es wird also die zweite Funktion zum shufflen der Signatur aufgerufen. Diese Funktion sieht in C so aus:

unsigned char op_value = signature2[3];
int i;
for(i = 0; i < 8; i++)
{
signature2[i] = signature2[i] ^ op_value;
}

es wird das 4te Byte der Signatur ausgelesen, und für die nachfolgenden Operationen verwendet. Nun wird jeder Wert der Signatur mit diesem Wert geXORed.

Das heißt quasi: Unsere aktuelle Signatur2 ist 41 42 43 44 45 46 47 00. Das dick markierte ist unser op_value. Zuerst XORed er also 0×41 mit 0×44. Dann 0×42 mit 0×44 usw. Irgendwann ist er aber bei sich selbst, er XORed also 0×44 mit 0×44. Wie jeder wissen sollte, ergibt das zwangsläufig immer 0×00. Das ist sehr fatal, wenn dieser Wert später in der Verschlüsselung Verwendung finden soll! Nach dem Shuffle haben wir eine Signatur2 die wie folgt aussieht: 05 06 07 00 01 02 03 44

Beachte: das letzte Byte ist 0×44, da hier 0×00 mit 0×44 geXORed wurde.

Nun, das ist der ganze Algorithmus. Nun machen wir uns daran, den Algorithmus anzugreiffen!

Wenn wir genau nachdenken, dann baut die Verschlüsselung darauf auf, dass der Pseudo-Ciphertext jeweils mit der zweiten Signatur XOR-verknüpft wird. Allerdings haben wir gerade gesehen, dass die zweite Signatur zwangsweise irgendwo eine 0×00 enthält. Irgendein Wert mit 0×00 geXORed ergibt wieder den Wert. Es passiert also nichts. Das heißt im Klartext: Ein Byte wird in dem Ursprungsformat bleiben, und nicht verändert.

Wenn ich mir jetzt in der fertig verschlüsselten Datei goddis.txt den nächsten 8-Byte Block anschaue, sehe ich das hier:

F2 F1 F0 F7 F6 F5 F4 B3

Wir erinnern uns, der Header bestand im zweiten Block nur aus 0xF7. Hier eine kleine bildliche Erläuterung:

Und da es ein XOR Verfahren ist, könnten die Pfeile auch eine Spitze auf beide Seiten haben. Denn ich kann auch Rückwärts von 81 durch XORen mit 0×41 wieder 0xC0 erhalten.

Ich sehe hier, dass beim XORen des zweiten Blockes, ein Fehler entsteht. Und zwar bleibt der 0xF7 bestehen, wie bereits erwähnt. Wenn wir nun die Ausgabe-Bytes (also F2 F1 F0 F7 F6 F5 F4 B3) wiederum mit 0xF7 alle XORen, haben wir die aktuelle Signatur2 (nämlich 05 06 07 00 01 02 03 44) errechnet. Aufgrund des 0×00 Byte-Bugs am Ende der Signatur, haben wir hier 0×44 (“D”) am Ende hängen. Wir wissen also, mit welchem Wert die Signatur geshuffled wurde. Wenn wir also alle signatur Bytes wieder mit 0×44 XORen, erhalten wir “ABCDEFG”. Tada!

Wir haben den zweiten Signatur-Key errechnet, dank der unzähligen Bugs im Algorithmus. Das hier vorgestellte Verfahren würde wohl in die Kategorie “Known Plaintext” fallen (beispielsweise wurde WEP dadurch gebrochen). Da wir in diesem Fall wissen, wie der Plaintext (hier eher Pseudo-Ciphertext) im Header aussieht (nämlich fast nur 0xF7er Bytes), können wir in jedem Fall den zweiten Signatur Key errechnen. Zur verdeutlichung:

Und mit diesem Key können wir nun sig1 entschlüsseln.

Wir haben allerdings ein Problem. Beim shufflen wird nicht immer XOR verwendet. Wir erinnern uns: es gibt 5 verschiedene Funktionen, um die Signatur zu shufflen. Eine davon verwendet ein “AND” zum verknüpfen, ein anderer ein “OR”.

Nehmen wir an, es wurde ein OR verwendet. Ich schaue mir diese shuffle Funktion an. Und als Operator wird das letzte Byte der Signatur verwendet. Allerdings ist das letzte Byte ja immer 0×00! D.h., beim shufflen werden alle Bytes der signatur mit 0×00 geORed. Das Resultat: es verändert sich nichts an der Signatur! Der nachfolgende 0xF7er Block wird mit der Original Signatur verschlüsselt. Wir XORen also wieder den Ciphertext mit 0xF7, und erhalten unsere original Signatur2. Das ist wieder ein trivialer Bug!

Anders sieht es aus, wenn “AND” zum shufflen benutzt wurde. Die passende Funktion dazu benutzt das 5te Byte der Signatur zum shufflen. Dieser könnte alles mögliche sein (alle printable ASCII Zeichen). Doch nicht verzagen, eddy fragen!

Bei einem AND können wir also nicht mit einer so einfachen Möglichkeit die Signatur2 errechnen. Aber wir können die aktuelle geshufflete Signatur für den 0xF7er Block errechnen, indem wir wieder diesen Ciphertext mit 0xF7 XORen. Und mit diesem Key rechnen wir ganz normal weiter.

Man sieht zwar noch meine Debug-Ausgaben, aber ich denke es ist ersichtlich, dass dieses Programm eine verschlüsselte Datei brechen kann, ohne zusätzliche Informationen, nur allein die verschlüsselte Datei! Wie toll ist das bitte? Hier der C-Quelltext (Bitte bitte entschuldigt den dreckigen Code! Es ist sehr schwer zu coden, und nebenbei die Analyse zu betreiben): disdis.c (hab jetzt absolut keine Lust die ganzen Debug Informationen zu entfernen…).

In diesem Blogpost liest man viele verschiedene Werte, und ich bin mir sicher, dass die meisten dem nicht folgen konnten und einfach nur verwirrt waren. Ich werde versuchen dazu ein paar Bilder mehr zu basteln (eventuell sogar eine Flash Animation?).

Wie auch immer; ich bin froh, denn: Der Verschlüsselungsalgorithmus ist gebrochen! :-)

If you enjoyed this article please consider staying updated via RSS. Links to your own social media pages could be added here.

5 Responses to “Disgu(i)sting”

  1. Hansi says:
    October 18th, 2010 at 22:55

    Welcome back!

  2. siriusroot says:
    October 19th, 2010 at 06:18

    Yay eddy <3

  3. Wolfii says:
    October 19th, 2010 at 18:27

    Schön mal wieder was von dir zu lesen !

    PS: Du konntest es mal wieder nicht lassen die Console mit grünem Text auszustatten oder ? ^^

    MfG. Wolfii.

  4. r00t says:
    October 20th, 2010 at 10:36

    Welcome Back Eddy,schön von dir zu lesen!

  5. napster-3D says:
    October 20th, 2010 at 18:44

    Welcome back <3 :)

    Huebscher Beitrag ;) .

    mfg,

Leave a Reply

I would love to hear your view.