Zum Inhalt springen
Alle Beiträge

12 Min. Lesezeit

Eine 40-Bit-Excel-Datei knacken: Der lange Weg und der richtige

Forensics, Cryptography, hashcat, Linux, WSL

TL;DR: Das „Kennwort zum Öffnen" von Excel 97-2003 nutzt RC4 mit einem absichtlich verkrüppelten 40-Bit-Schlüssel, also spielen Länge und Komplexität des Passworts keine Rolle: Der Schlüsselraum ist ein festes 2^40, das man immer erschöpfen kann. Ich habe eine Nacht an hashcats Collider verbrannt (er fand den Schlüssel, konnte ihn aber nicht einlösen), bevor ein zweckgebautes Werkzeug, kholia/RC4-40-brute-office, denselben Schlüssel über Nacht per Brute-Force fand und crackxls2003 die Datei entschlüsselte. Die Moral: Greif den Schlüssel an, nicht das Passwort. Und wenn ein allgemeines Werkzeug ständig versagt, greif zum spezifischen.

Ich hatte eine alte .xls-Datei, die ich öffnen musste, und kein Passwort dafür. Was ein Ein-Stunden-Job hätte sein sollen, wurde zu einer ganzen Nacht, die meiste Zeit davon damit verbracht zu lernen, dass „garantiert funktioniert" und „funktioniert tatsächlich" zwei verschiedene Behauptungen sind. Das hier ist das ehrliche Protokoll: der Fehler, der diese Dateien knackbar macht, die Werkzeug-Kaninchenbaue, in die ich hineingefallen bin, und das kleine Open-Source-Programm, das den Job leise erledigte, während ich schlief.

Die Datei, und warum die üblichen Tricks scheitern

Das Ziel war eine Excel-97-2003-Tabelle mit gesetztem „Kennwort zum Öffnen". Das ist wichtig, denn Excel hat zwei sehr unterschiedliche Arten von Schutz.

Blatt- oder Arbeitsmappenschutz ist auf UI-Ebene. Die Daten liegen offen sichtbar in der Datei, und die „Sperre" sagt Excel bloß, es solle beim Bearbeiten höflich sein. Das kann man entfernen, indem man die Datei entpackt und eine Zeile XML löscht. Jedes „Excel-Passwort in 30 Sekunden entfernen"-Tutorial handelt von diesem Fall.

„Kennwort zum Öffnen" ist echte Verschlüsselung. Die gesamte Nutzlast der Datei ist verschlüsselt, es gibt also nichts zu entpacken und kein XML zu bearbeiten. LibreOffice, Online-Viewer und Hex-Editoren laufen alle gegen dieselbe Wand: ohne den Schlüssel sind die Bytes nur Rauschen.

Das war also ein Entschlüsselungsproblem, kein Schlösserknacken.

Der Fehler, der es gewinnbar macht

Hier kommt der Teil, der die ganze Aufgabe neu einordnet, und das Wichtigste, was man verstehen sollte, bevor man Zeit verschwendet.

Excel 97-2003 verschlüsselt mit RC4 und einem 40-Bit-Schlüssel. Wegen der US-Exportbeschränkungen für Kryptografie aus den 1990ern wurde der Schlüssel absichtlich verkrüppelt. Der Schlüssel wird so abgeleitet: Das Passwort wird UTF-16-kodiert, MD5-gehasht, und nur die ersten 5 Bytes dieses Hashes fließen zusammen mit dem Dokument-Salt in den RC4-Key-Schedule ein.

Fünf Bytes. Vierzig Bit. Etwa 1,1 Billionen mögliche Schlüssel.

Die Konsequenz ist genau das, worüber die Leute stolpern: Länge und Komplexität des Passworts sind irrelevant. Ein 3-Zeichen-Passwort und eine 30-Zeichen-Passphrase kollabieren beide in denselben 40-Bit-Schlüsselraum. Greift man das Passwort an, durchsucht man einen unbegrenzten Raum. Greift man den Schlüssel an, durchsucht man einen festen, endlichen, garantiert-endenden Raum von 2^40.

Diese Unterscheidung ist die ganze Geschichte der Nacht. Ich habe sie immer wieder vergessen, und die Werkzeuge ließen mich sie vergessen.

Schritt 1: Den Hash extrahieren

office2john.py (aus der John-the-Ripper-Jumbo-Suite) liest die Datei und gibt die Parameter in einem Format aus, das die Cracking-Tools verstehen:

python3 office2john.py file.xls > hash.txt

Die rohe Zeile sieht so aus (Werte gekürzt), und es lohnt sich zu wissen, was jedes Feld ist:

file.xls:$oldoffice$0*90b04d67...*0d72a8e2...*6c86895e...:::::file.xls
         └────┬────┘ │ └───┬───┘  └───┬───┘  └───┬───┘└─┬─┘
           type      │   salt      verifier   verifier   trailing
        ($oldoffice) │            (encrypted)   hash     junk from
                     └ algo: 0 = MD5+RC4-40                office2john

Die 0 nach $oldoffice$ ist die Kennung für MD5 + RC4-40, den schwachen Standardfall. ($oldoffice$3 oder $4 würden SHA1 + RC4 bedeuten, eine etwas andere Modus-Familie.) Das Salt und die zwei Verifier-Blöcke sind das, wogegen ein Cracker einen Schlüsselkandidaten prüft: Schlüssel ableiten, Verifier entschlüsseln, hashen und schauen, ob es zum Verifier-Hash passt.

Gut zu wissen vorweg: office2john hängt den Dateinamen und ein paar leere Felder mit ::::: an. Mehrere Tools verschlucken sich an diesem Anhang, also habe ich ihn vor der Übergabe an hashcat auf das reine Token gekürzt:

# keep only the $oldoffice$...*...*...*... part, drop the :::::filename tail
grep -o '\$oldoffice\$[^:]*' hash.txt > hash_clean.txt

Schritt 2: Der GPU-Umweg, den ich hätte auslassen sollen

Mein erster Reflex war hashcat auf einer GPU. Meine erste Maschine, ein älteres Laptop mit einer NVIDIA MX350, hatte andere Pläne.

cuInit(): no CUDA-capable device is detected
clGetPlatformIDs(): CL_PLATFORM_NOT_FOUND_KHR

Überhaupt kein Compute-Backend. Der Debugging-Baum, für alle, die von einer Suche hier landen:

nvidia-smi
# "couldn't communicate with the NVIDIA driver"  -> module not loaded
 
mokutil --sb-state          # SecureBoot disabled  -> not the cause
lsmod | grep -E 'nvidia|nouveau'   # empty          -> nothing loaded at all
dpkg -l | grep nvidia-driver       # nvidia-driver-535 IS installed
dkms status                        # "command not found" -> no DKMS

Die Diagnose: Das Treiber-Metapaket war vorhanden, aber das Kernelmodul war nie für den laufenden Kernel gebaut worden, und ohne DKMS gab es nichts, was es neu gebaut hätte. Klassischer Bruch nach einem Kernel-Update. Die Lösung, hätte ich die GPU gewollt, wäre gewesen:

sudo apt install -y dkms build-essential linux-headers-$(uname -r)
sudo apt install --reinstall nvidia-driver-535
sudo reboot

Ich hätte es reparieren können. Aber das war ein Laptop mit einer Geschichte von Wärmeproblemen, und ein 40-Bit-Job braucht in Wahrheit keine GPU. Also traf ich die pragmatische Entscheidung: Rückzug auf die CPU über PoCL (eine portable OpenCL-Laufzeit), die jede moderne CPU antreiben kann.

sudo apt install -y pocl-opencl-icd ocl-icd-libopencl1
./hashcat.bin -I    # now lists the CPU as an OpenCL device

Das funktionierte. Es war auch langsam, aber an dem Punkt wollte ich einfach nur ein Backend.

Schritt 3: hashcat, und der Collider, der sich nie auszahlte

Für $oldoffice$0 hat hashcat drei relevante Modi:

  • 9700 knackt das Passwort direkt.
  • 9710 / 9720 sind „Collider"-Modi, die den 40-Bit-Schlüssel direkt wiederherstellen, was der begrenzte, garantierte Ansatz ist.

Da ?b ein volles Byte ist (0x00 bis 0xff), ist ?b?b?b?b?b genau der 2^40-Schlüsselraum. Der Plan war also der Collider: erst 9710, dann 9720.

# stage 1: collider #1, recovers an intermediate value
hashcat -m 9710 -a 3 hash_clean.txt ?b?b?b?b?b
 
# stage 2: collider #2, meant to turn that into a working key
hashcat -m 9720 -a 3 hash_clean.txt ?b?b?b?b?b

Zuerst wehrt sich das Eingabeformat. hashcat wies den Hash mit Token length exception zurück, wegen des :::::filename-Anhangs (genau dafür war der grep -o-Trim oben da).

9710 lief und knackte bei etwa 32 % des Raums:

$oldoffice$0*...ba37d:c1e0600e51

c1e0600e51. Endlich Fortschritt. Nur dass 9720, die zweite Stufe, die das in einen funktionierenden Schlüssel verwandeln soll, den gesamten Schlüsselraum erschöpfte und nichts fand. Ich habe eine peinlich lange Zeit mit dem Übergabeformat gekämpft (der zweistufige Collider ist heikel, und jeder Versuch brachte einen neuen Windows-Textkodierungs-Kobold zum Vorschein: hier ein UTF-16-BOM, dort eine PowerShell->-Umleitung, die Bytes verstümmelt). Ich habe auch zuerst eine Wortliste probiert, für den Fall, dass das Passwort banal war. Das sind immer die zwei Minuten wert:

# rockyou, then rockyou mangled by a rule set, both against password mode 9700
hashcat -m 9700 -a 0 hash_clean.txt rockyou.txt
hashcat -m 9700 -a 0 hash_clean.txt rockyou.txt -r rules/top10_2025.rule

Beide verfehlten. Dann ein voller -m 9700-Brute-Force mit ?b?b?b?b?b, der ebenfalls erschöpfte, weil dieser Modus 5-Byte-Passwörter durchprobiert, nicht den Schlüssel, und das Passwort war schlicht nicht 5 Bytes lang.

Zwei volle, korrekt aussehende Durchläufe. Null Ergebnisse. Das sollte für eine Datei, die wirklich 40-Bit-RC4 verwendet, nicht passieren.

Die Lektion, für die ich hier bezahlt habe: Dass ein Werkzeug theoretisch garantiert ist, heißt nicht, dass seine Implementierung bei deiner Datei und deiner Version liefert. hashcats Collider ist clever, aber auf diesem Build produzierte er einen Wert, den er nicht einlösen konnte. Ich hatte Stunden damit verbracht, das beliebte Werkzeug das Problem definieren zu lassen.

Schritt 4: Umzug auf echte Hardware (und die GPU gratis dazu)

Irgendwann mittendrin verlagerte ich den Job auf einen Mini-PC mit einem Ryzen 7 8845HS und dessen integrierter Radeon 780M. Zwei Lektionen aus diesem Umzug, beide es wert:

hashcat sah die 780M sofort über OpenCL (ignoriere die lärmenden Failed to initialize AMD HIP-Warnungen; HIP ist ein anderes Backend, und OpenCL ist alles, was du brauchst). Auf dem Collider lief es mit 200 bis 700 MH/s gegenüber etwa 5 MH/s auf der alten Laptop-CPU. Wenn du eine GPU nutzen kannst, ist der Unterschied nicht subtil.

Aber es spielte keine Rolle, denn der Collider erschöpfte trotzdem. Schneller falsch ist immer noch falsch.

Unterwegs habe ich auch die volle Sammlung an Windows-Shell-Papierschnitten eingesammelt, hier dokumentiert, damit du sie nicht wiederholst:

# 1. PowerShell won't run a binary in the current dir without .\
hashcat.exe -m 9700 ...      # "not recognized as a cmdlet"
.\hashcat.exe -m 9700 ...    # works
 
# 2. PowerShell aliases curl -> Invoke-WebRequest, which has no -L
curl -L -o john.7z <url>     # "A parameter cannot be found that matches -L"
curl.exe -L -o john.7z <url> # calls the real curl, works
 
# 3. `>` redirection writes UTF-16 + BOM, which tools like John reject
python office2john.py file.xls > hash.txt          # -> "UTF-16 BOM seen in input file"
python office2john.py file.xls | Out-File hash.txt -Encoding ascii   # plain bytes
# most reliable: write the exact string with .NET, no BOM, no trailing newline
powershell -Command "[IO.File]::WriteAllText('C:\path\hash.txt', '$oldoffice$0*...')"

Schritt 5: Zurück zu den Grundprinzipien

Zwei saubere, erschöpfende Durchläufe drängten mich, der Rahmung nicht mehr zu trauen und zu fragen, was definitiv funktioniert. Die Antwort war ein zweckgebautes Werkzeug, das die eine ehrliche Sache tut: den 40-Bit-Schlüsselraum durchsuchen und jeden Kandidatenschlüssel gegen den eigenen verschlüsselten Verifier der Datei prüfen, dann entschlüsseln.

Zwei Open-Source-Optionen, beide von Dhiru Kholia (derselbe Autor wie office2john, was zählt, weil der Hash-Extraktor und der Cracker dann ein gemeinsames Verständnis des Formats teilen):

  • kholia/RC4-40-brute-office, das den $oldoffice$-Hash direkt entgegennimmt.
  • GavinSmith0123/crackxls2003, das auf der Datei arbeitet und, bei gegebenem Schlüssel, auch entschlüsseln kann.

Ich habe RC4-40-brute-office in WSL auf dem Mini-PC gebaut (die Linux-Toolchain war von vorhin schon da):

git clone https://github.com/kholia/RC4-40-brute-office.git
cd RC4-40-brute-office
make
./RC4-40-brute 'file.xls:$oldoffice$0*90b04d67...*0d72a8e2...*6c86895e...'

Es begann sofort, Fortschritt auszugeben:

0 0 @ 08-07-2026  01:28:32
0 1 @ 08-07-2026  01:28:33
...

Die Fortschrittsausgabe lesen

Diese zwei Zahlen sind die ersten zwei Bytes des 5-Byte-Schlüssels. Die anderen drei Bytes werden in der inneren Schleife zwischen den gedruckten Zeilen still durchlaufen. Jede Zeile steht also für 256 × 256 × 256 ≈ 16,7 Millionen getestete Schlüssel, und ein voller Lauf sind 256 solcher Zeilenzyklen, d. h. der ganze 2^40-Raum. Es ist ein Kilometerzähler, bei dem nur die obersten zwei Stellen sichtbar sind.

Nutzt es überhaupt alle Kerne?

Die Rate sah nach Single-Thread aus, also habe ich nachgeschaut, statt es anzunehmen:

ps aux | grep -i rc4
# ... 1560 ...  ./RC4-40-brute ...

Diese dritte Spalte war 1560 % CPU: etwa 15 von 16 Threads. Der -fopenmp-Build hatte die heiße Schleife parallelisiert; sie lief schon am Anschlag. Nichts zu optimieren.

Die echte ETA, aus der Maschine berechnet

Bei etwa 3 Minuten pro Inkrement des obersten Bytes über 256 davon war der volle Durchlauf im schlimmsten Fall etwa 13 Stunden, und da der Schlüssel mit gleicher Wahrscheinlichkeit überall sitzen kann, im Schnitt etwa 6 bis 7 Stunden. Ein Job über Nacht, also ließ ich ihn unter screen laufen, damit er das Abreißen der SSH-Sitzung überlebte:

screen -S crack
# run ./RC4-40-brute ... inside, then detach with  Ctrl+A  then  D
# reattach any time with:
screen -r crack

Der Lohn

Am Morgen:

193 224 @ 08-07-2026  11:39:34
Key is : c1e0600e51

c1e0600e51. Der exakte Wert, den hashcats 9710 mir Stunden zuvor gereicht hatte. Das ist die ganze Moral der Nacht in einer Zeile: Ich hatte die Antwort früh gehabt und mir fehlte schlicht ein Werkzeug, das damit zum Abschluss kommen konnte. hashcat fand den Schlüssel und konnte nicht entschlüsseln; dieses Werkzeug fand denselben Schlüssel und verifizierte ihn gegen die Datei. Das ist der Schritt, auf den es tatsächlich ankommt.

Die Entschlüsselung selbst war ein Befehl mit crackxls2003. Es baut sich mit dem üblichen Autotools-Tanz und braucht OpenSSL sowie libgsf für die Entschlüsselungsfunktion:

git clone https://github.com/GavinSmith0123/crackxls2003.git
cd crackxls2003
sudo apt install -y libssl-dev libgsf-1-dev
./configure && make

Dann füttert man es mit dem wiederhergestellten Schlüssel als 5 durch Leerzeichen getrennte Hex-Bytes (c1e0600e51 wird zu c1 e0 60 0e 51), der Eingabedatei und einem Ausgabepfad:

./crackxls2003 -d "c1 e0 60 0e 51" file.xls file-DECRYPTED.xls
Data successfully loaded from file.xls
Password salt is 90 b0 4d 67 ...
Encrypted verifier is 0d 72 a8 e2 ...
Output file-DECRYPTED.xls

Es öffnete sich in Excel ohne Abfrage. Fertig.

Fazit

40-Bit-RC4 ist kaputt per Design, nicht aus Versehen. Es wurde verkrüppelt, um dem Exportrecht der 1990er zu genügen, und für die „kompatiblen" Formate nie repariert. Jede Datei, die es benutzt, ist in begrenzter Zeit wiederherstellbar, Punkt. Wenn du dich für irgendetwas Sensibles auf das „Kennwort zum Öffnen" von Excel 97-2003 verlässt, hör damit auf.

Greif den Schlüssel an, nicht das Passwort. Das Passwort kann beliebig lang sein; der Schlüssel hat immer 40 Bit. Jede Stunde, die ich in passwortförmige Angriffe steckte, war eine Stunde im falschen, unbegrenzten Raum.

„Garantiert" ist eine Aussage über Mathematik, nicht über die Binärdatei vor dir. hashcats Collider ist auf dem Papier garantiert und erschöpfte auf meiner Datei zweimal. Das Werkzeug, das funktionierte, war kleiner, einzweckig und testete Kandidaten gegen die Datei selbst. Wenn ein allgemeines Werkzeug ständig versagt, greif zum spezifischen.

Passe deine Werkzeuge zur Herkunft des Formats. Der siegreiche Cracker und der Hash-Extraktor kamen vom selben Autor. Sie stimmten bei der Ableitung überein, wo unabhängige Implementierungen es nicht taten.

Sofort neu speichern. Sobald sie offen ist, speichere sie als modernes .xlsx mit echter Verschlüsselung (oder bewusst ohne). Lass nichts, was dir wichtig ist, hinter Krypto aus der Export-Ära liegen.

Die saubere Version dieses Jobs ist: Hash extrahieren, einen 40-Bit-Schlüssel-Brute-Forcer laufen lassen, mit dem Schlüssel entschlüsseln. Drei Schritte. Ich habe etwa zwanzig gebraucht, um dorthin zu kommen, und die meisten Umwege waren ich, der dem beliebten Werkzeug mehr traute als dem richtigen. Für alle, die einfach nur den glücklichen Pfad wollen:

# 1. extract the hash
python3 office2john.py file.xls > hash.txt
 
# 2. brute-force the 40-bit key (guaranteed, ~hours on a modern multicore CPU)
git clone https://github.com/kholia/RC4-40-brute-office.git
cd RC4-40-brute-office && make
./RC4-40-brute "$(cat ../hash.txt)"      # prints:  Key is : c1e0600e51
 
# 3. decrypt with the recovered key (split into hex byte pairs)
cd .. && git clone https://github.com/GavinSmith0123/crackxls2003.git
cd crackxls2003
sudo apt install -y libssl-dev libgsf-1-dev && ./configure && make
./crackxls2003 -d "c1 e0 60 0e 51" ../file.xls ../file-DECRYPTED.xls

Keine GPU, kein hashcat, kein Collider. Nur der endliche Schlüsselraum und ein Werkzeug, das jeden Kandidaten gegen die Datei prüft.

Alle Beiträge