Ein praktisches Vorgehen zur Identifizierung des Verfahrens hinter einem Hash-String
· Go Komura · Hash, Sicherheit, Passwörter, Nutzung bestehender Assets, Technische Untersuchung
Es gibt ziemlich viele Situationen, in denen man einen String wie 5f4dcc3b5aa765d61d8327deb882cf99 oder $2b$12$..., der in Logs oder einer Datenbank übrig geblieben ist, betrachtet und feststellen möchte, „was für ein Hash das eigentlich ist“. Bei der Migration bestehender Systeme, der Untersuchung von Authentifizierungsverfahren, der Log-Analyse oder der Anbindung an Fremdsysteme bleibt man hier nicht selten hängen.
Gefährlich ist dabei allerdings, allein aufgrund der Länge vorschnell zu urteilen.
Bei einem 64-stelligen Hex-String zu sagen „das ist SHA-256“ ist verfrüht. SHA3-256, SHA-512/256, BLAKE2s-256 und die Standardausgabe von BLAKE3 mit 32 Byte können dieselbe Länge ergeben. Umgekehrt lassen sich Speicherformate wie $2b$ oder $argon2id$, die sogar Präfix und Parameter enthalten, allein aus dem String mit ziemlich hoher Genauigkeit bestimmen.
In diesem Artikel verwenden wir das Wort hash breiter gefasst: Neben Message-Digests wie MD5 / SHA-2 / SHA-3 behandeln wir auch String-Darstellungen für die Passwortspeicherung wie bcrypt / scrypt / Argon2 / PBKDF2.
Der Inhalt orientiert sich an offiziellen Quellen wie RFCs, NIST-Publikationen, Linux crypt(5), Apache, Django und Spring Security, die mit Stand April 2026 veröffentlicht sind.
Die Zielgruppe sind Personen, die für die Migration bestehender Systeme, die Untersuchung von Authentifizierungsverfahren oder die Log-Analyse zuständig sind und herausfinden möchten, was ein vorliegender „hash-artiger String“ eigentlich ist. An Vorwissen reicht es, zu wissen, was hex-Notation und Base64 sind.
Wenden Sie das Vorgehen in diesem Artikel bitte nur auf Strings aus Umgebungen an, die Sie selbst verwalten oder für die eine Untersuchung ausdrücklich erlaubt wurde. Das Auslesen des Passwort-Hashes eines fremden Kontos oder das Analysieren von Hashes aus einem System ohne Berechtigung lässt sich nicht rechtfertigen, selbst wenn der Zweck eine Untersuchung ist. Auch die im weiteren Verlauf gezeigten Verifizierungsbefehle gehen davon aus, dass Sie sie an Testkonten oder selbst erstellten Beispielen ausprobieren.
Inhaltsverzeichnis
- Zuerst das Fazit
- Identifizierungstabellen auf einen Blick
- Praktisches Vorgehen bei der Identifizierung
- Häufige Fehleinschätzungen
- Prüfreihenfolge für hundertprozentige Gewissheit
- Zusammenfassung
- Zu diesem Thema passende Leistungen
- Quellen
1. Zuerst das Fazit
Vorab kurz das Fazit.
-
Speicherformate mit Präfix oder Trennzeichen lassen sich allein aus dem String recht gut bestimmen. Beispiel:
$argon2id$...,$2b$...,$5$...,$6$...,{SHA}...,pbkdf2_sha256$... -
Reine Hex-Strings oder bloßes Base64 bringen meist nur bis zur „Eingrenzung der Kandidaten“. Beispiel:
32 hex = könnte MD5 sein, könnte aber auch von MD4 oder einem NT-Hash stammen -
Die Zeichenart ist ebenso wichtig wie die Länge. Bei
+/=liegt RFC-4648-Base64 nahe, bei einem.zusammen mit$-Trennung liegt diecrypt(3)-Familie nahe - solche Unterscheidungen funktionieren gut. -
Für hundertprozentige Gewissheit braucht es Kontext. Ob der String in
/etc/shadow,.htpasswd, Djangosauth_useroder Spring Security liegt, ändert die Einschätzung.
Kurz gesagt: „Verfahren, die allein am String erkennbar sind“ und „Verfahren, bei denen der String nur eine Kandidatenmenge liefert“ sind zwei verschiedene Dinge. Schon diese Trennung im Kopf verändert, wie eine Untersuchung verläuft.
2. Identifizierungstabellen auf einen Blick
Bevor wir die Tabelle lesen, klären wir vier Begriffe, die in diesem Artikel als „Formatnamen“ auftauchen. Alle werden in ähnlichem Kontext verwendet, bezeichnen aber unterschiedliche Dinge.
| Begriff | Ausgeschrieben | Bedeutung |
|---|---|---|
crypt(3) |
- | Die Unix-Passwort-Hash-Funktion selbst. Der Name der C-Bibliotheksfunktion, die im man-Kapitel 3 (Bibliotheksfunktionen) beschrieben ist - daher diese Schreibweise |
crypt(5) |
- | Die man-Seite, die das String-Format beschreibt, das diese Funktion liest und schreibt. Sie steht im man-Kapitel 5 (Dateiformate); ein Format wie $6$salt$hash ist dort dokumentiert |
| MCF | Modular Crypt Format | Der gebräuchliche Name für die Schreibweise „am Anfang steht $id$, um das Verfahren anzuzeigen“. Es gibt keine einzelne Spezifikation dafür - der Name hat sich als Konvention etabliert, während immer mehr crypt(3)-Implementierungen entstanden |
| PHC string format | Password Hashing Competition string format | Eine Spezifikation, die MCF neu geordnet hat. Sie legt bis hin zur Schreibweise von Version und Parametern fest, etwa $argon2id$v=19$m=65536,t=3,p=4$salt$hash |
Grob gesagt: crypt(3) ist die Funktion, crypt(5) ist die Spezifikation ihres Ausgabeformats, MCF ist der gebräuchliche Name für dieses Format, und das PHC string format ist dessen neu formalisierte Fassung. Wenn in den folgenden Tabellen „PHC string format“ oder „crypt-Familie“ steht, lesen Sie es mit dieser Unterscheidung.
2.1 Formate, die sich über Präfix oder Formatmarker fast eindeutig bestimmen lassen
„Konfidenz“ wird in der Tabelle in diesem Sinn verwendet.
- Stark: Allein aus dem String fast eindeutig bestimmbar
- Mittel: Kandidaten lassen sich deutlich eingrenzen, aber auf Implementierungsunterschiede ist zu achten
- Schwach: Über Länge oder Erscheinungsbild allein nicht sicher zu bestimmen
| Erscheinungsbild | Erster Verdacht | Konfidenz | Anmerkung | Beispiel |
|---|---|---|---|---|
$argon2id$... |
Argon2id | Stark | PHC string format. Häufig folgen v=, m=, t=, p= |
$argon2id$v=19$m=65536,t=3,p=4$MDEyMzQ1Njc4OWFiY2RlZg$uKZLaN6muIyoyIYr5waqw3y+zaDbe9aLSPj6Ln/rbz4 |
$argon2i$... |
Argon2i | Stark | Wie oben | $argon2i$v=19$m=65536,t=3,p=4$MDEyMzQ1Njc4OWFiY2RlZg$Kx1koF/7n8EytGJYTS5krh+ag+FlG5ksM4xOsjOSDvo |
$argon2d$... |
Argon2d | Stark | Wie oben | $argon2d$v=19$m=65536,t=3,p=4$MDEyMzQ1Njc4OWFiY2RlZg$HLIGA+T1bwK8akx3LGOco+Df+PvxX6cIXhycO7O7t6c |
$2a$... / $2b$... / $2y$... |
bcrypt | Stark | 2-stelliger Kostenfaktor + crypt-typisches alphabet | $2b$12$9YQ2u/e5Y/ArOnG.gJKxK.0makLATcYLP1q.Nsabzrw7XErYCfoYO |
$1$... |
md5crypt | Stark | Unix-typisches MD5-Passwortspeicherformat | $1$vA7mQ9xZ$Erz32JUFnZ9991KdU5.N3. |
$5$... |
sha256crypt | Stark | Kein reines SHA-256 | $5$rounds=5000$N3v8Kx2Lq9Rt$uOTla5GAHaRH2aHlUSjkrZUBCuFiahQZ36O/seB39r3 |
$6$... |
sha512crypt | Stark | Kein reines SHA-512 | $6$rounds=5000$N3v8Kx2Lq9Rt$6LUcSUAELX3aC/.60pTB.TFLTQi1mOGRCwKqNCqtRSaXjorxj01HJ9oNni97Kci1uDt7a/Kn4t3OS20Dw/.vi1 |
$7$... |
scrypt (crypt-Familie) | Stark | In Linux-Implementierungen der crypt(5)-Familie zu finden |
$7$CU..../....k2XAnEHBqQ1Ct2aMXFKNa/$y3Q0e/UlCHacIGWQshgvvz6UIbP.BCja.5BfVWP2Ml8 |
$y$... |
yescrypt | Stark | Auf neueren Linux-Systemen zu finden | $y$j9T$k2XAnEHBqQ1Ct2aMXFKNa/$OVYXzjlkiQpWT/F1CUE0JrvV4phLY8FB.ofDttnrSQ7 |
$apr1$... |
Apache APR1-MD5 | Stark | Häufig in .htpasswd zu finden |
$apr1$vA7mQ9xZ$ZE64.ohiyK11sPZmtnJZQ. |
{SHA}... |
Base64-Darstellung eines SHA-1-Digests | Stark | Häufig im Apache-/LDAP-Umfeld zu finden | {SHA}VBPuJHI7uixaa6LQGWx4s+5GKNE= |
{SSHA}... |
salted SHA-1 | Stark | LDAP-Familie | {SSHA}/OczD0GNNkOAUPbYhA3L9fjmcyBCbHVlTWVzYTQyIQ== |
{MD5}... / {SMD5}... |
MD5 / salted MD5 | Stark | LDAP-Familie | {MD5}X03MO1qnZdYdgyfeuILPmQ=={SMD5}fOn1rOv4ZH0OrO/KT9H0fEJsdWVNZXNhNDIh |
pbkdf2_sha256$... |
PBKDF2-HMAC-SHA256 | Mittel-Stark | Bei Django und ähnlichen Implementierungen wird der Formatname vorangestellt | pbkdf2_sha256$600000$N3v8Kx2Lq9Rt$CLxGB+zTiV1IdOt2y4m9JpaAONzHuRTOd96xKQwRQAs |
{bcrypt}$2b$... |
bcrypt | Stark | Mit Spring Securitys {id}-Wrapper versehen |
{bcrypt}$2b$12$9YQ2u/e5Y/ArOnG.gJKxK.0makLATcYLP1q.Nsabzrw7XErYCfoYO |
{pbkdf2}... / {scrypt}... |
Verfahren mit Implementierungslabel | Mittel-Stark | Bei Spring Security und ähnlichen erkennt man eher das Wrapper-Format als den zugrundeliegenden Algorithmus | {pbkdf2}sha256$600000$Qmx1ZU1lc2E0MiE$4eNuai1qNkgs1kXz3+tBUMzAexVsSUz9SrQKEhbk0Cw{scrypt}ln=14,r=8,p=1$Qmx1ZU1lc2E0MiE$xAgBRhXbMtHB1UHUR0br5bI+1XdXWKbwauiFv5VRQBY |
Der entscheidende Punkt bei dieser Tabelle ist, dass Formate, bei denen die ersten Zeichen bereits eine Bedeutung tragen, stark sind.
Vor allem mit $...$ abgegrenzte Strings gehören mit hoher Wahrscheinlichkeit zur Unix-crypt(3)-/MCF-/PHC-Familie, und es ist schneller, zuerst den prefix zu prüfen als die Länge.
2.2 Tabelle zur Eingrenzung anhand der Länge bei reinem Hex / Base64
Diese Tabelle gilt für „reine digest-Strings“ ohne prefix.
Bei Darstellungen mit :, - oder Leerzeichen entfernt man zuerst die Trennzeichen und zählt dann die Länge.
| Rohe Byte-Länge | Hex-Zeichenzahl | Base64-Zeichenzahl (mit / ohne =) |
Hauptkandidaten | Beispiel |
|---|---|---|---|---|
| 4 | 8 | 8 / 6 | Prüfsummen wie CRC32 | cbf43926 |
| 16 | 32 | 24 / 22 | MD5, MD4, NT-Hash (MD4-basiert) | 5f4dcc3b5aa765d61d8327deb882cf99 |
| 20 | 40 | 28 / 27 | SHA-1, RIPEMD-160 | da39a3ee5e6b4b0d3255bfef95601890afd80709 |
| 28 | 56 | 40 / 38 | SHA-224, SHA-512/224, SHA3-224 | d14a028c2a3a2bc9476102bb288234c415a2b01f828ea62ac5b3e42f |
| 32 | 64 | 44 / 43 | SHA-256, SHA-512/256, SHA3-256, BLAKE2s-256, die Standardausgabe von BLAKE3 mit 32 Byte | e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 |
| 48 | 96 | 64 / 64 | SHA-384, SHA3-384, BLAKE2b-384 | 38b060a751ac96384cd9327eb1b1e36a21fdb71114be07434c0cc7bf63f6e1da274edebfe76f65fbd51ad2f14898b95b |
| 64 | 128 | 88 / 86 | SHA-512, SHA3-512, BLAKE2b-512, Whirlpool | cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e |
So liest man die Spalte „Base64-Zeichenzahl“: Diese Spalte zeigt zwei Varianten nebeneinander - mit und ohne =-Padding. Da RFC-4648-Base64 auf 4-Zeichen-Blöcke ausgerichtet ist, hängen nur dann 1-2 =-Zeichen am Ende an, wenn die rohe Byte-Länge kein Vielfaches von 3 ist. Bei JWTs oder der Einbettung in URLs lassen viele Implementierungen dieses = weg, sodass derselbe digest sowohl 43 als auch 44 Zeichen haben kann. Ist die Byte-Länge dagegen ein Vielfaches von 3, wie bei 48 Byte, entsteht kein Padding, sodass beide Zahlen gleich sind, hier 64 / 64. Prüfen Sie beim Eingrenzen über die Länge unbedingt gegen beide Zahlen.
Wichtig ist hier, dass eine übereinstimmende Länge das Verfahren nicht eindeutig festlegt. Vor allem bei 32-, 64- und 128-stelligem Hex gibt es viele Kandidaten, und eine Festlegung allein darauf geht leicht daneben.
2.3 Typische Beispiele, die zu Verwechslungen führen
| Erscheinungsbild des Strings | Häufiger Schnellschluss | Tatsächlich richtige Lesart | Beispiel |
|---|---|---|---|
5f4dcc3b5aa765d61d8327deb882cf99 |
Steht fest: MD5 | Sieht nach MD5 aus, könnte aber auch MD4, ein NT-Hash oder eine app-spezifische MD5-Verwendung sein | 8846f7eaee8fb117ad06bdd830b7586c |
64 hex wie 2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824 |
Steht fest: SHA-256 | SHA-256 ist ein Kandidat, aber auch SHA3-256 / SHA-512/256 / BLAKE2s-256 / BLAKE3 sind möglich | e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 |
$6$rounds=5000$salt$hash |
Hex-Darstellung von SHA-512 | Nein, sondern ein Passwort-Hash-String namens sha512crypt | $6$rounds=5000$N3v8Kx2Lq9Rt$6LUcSUAELX3aC/.60pTB.TFLTQi1mOGRCwKqNCqtRSaXjorxj01HJ9oNni97Kci1uDt7a/Kn4t3OS20Dw/.vi1 |
{SHA}VBPuJHI7uixaa6LQGWx4s+5GKNE= |
Irgendein „SHA“ | Im Apache-/LDAP-Umfeld bedeutet das meist einen Base64-codierten SHA-1-digest | {SHA}VBPuJHI7uixaa6LQGWx4s+5GKNE= |
{bcrypt}$2b$12$... |
Ein eigenes Verfahren namens {bcrypt} |
bcrypt, versehen mit dem Wrapper von Spring Security | {bcrypt}$2b$12$9YQ2u/e5Y/ArOnG.gJKxK.0makLATcYLP1q.Nsabzrw7XErYCfoYO |
3. Praktisches Vorgehen bei der Identifizierung
Ab hier gehen wir Schritt für Schritt durch, wie man tatsächlich vorgeht. Empfohlen ist die Reihenfolge prefix → Trennzeichen → Zeichenart → Länge → Kontext.
3.1 Zuerst die führenden Zeichen betrachten
Schon die ersten ein bis zehn Zeichen grenzen ziemlich stark ein.
-
$argon2id$/$argon2i$/$argon2d$Argon2s PHC string format liegt stark nahe. Die Bestandteile lassen sich in der SpalteBeispielvon 2.1 gut nachvollziehen. -
$2a$/$2b$/$2y$bcrypt liegt stark nahe. -
$1$/$5$/$6$/$7$/$y$Ein Unix-crypt(3)-typischer password hash liegt nahe. -
{SHA}/{SSHA}/{MD5}/{SMD5}Eine LDAP-/Apache-typische Darstellung liegt nahe. -
{bcrypt}/{pbkdf2}/{scrypt}Ein implementierungsgelabeltes Speicherformat wie bei Spring Security liegt nahe.
Der Trick besteht darin, nicht nur den eigentlichen Algorithmus, sondern auch das Speicherformat zu betrachten.
$6$ ist zum Beispiel nicht „ein SHA-512-digest“, sondern „ein password-hash-String, der SHA-512 verwendet“. Wer das verwechselt, gerät im weiteren Verlauf der Untersuchung auf Abwege.
3.2 Die Anzahl der Trennzeichen betrachten
Als Nächstes betrachtet man Trennzeichen wie $, :, {}, , und =.
-
Mehrere
$-Zeichen Ein Format, das Parameter, salt und hash gemeinsam trägt, liegt nahe. Argon2, bcrypt, sha256crypt und sha512crypt sind typische Beispiele. -
Beginnt mit
{name}Ein Wrapper, der das Verfahren ausdrücklich benennt, wie bei LDAP oder Spring Security, liegt nahe. -
Formen wie
algo:salt:hashoderalgo$iterations$salt$hashEin framework- oder app-spezifisches Format liegt nahe. Djangospbkdf2_sha256$iterations$salt$hashist das klassische Beispiel.
Je mehr Trennzeichen ein String hat, desto leichter lässt sich das Verfahren bestimmen. Steht dagegen nur ein einziger Block aus Hex oder Base64 da, bleibt es ziemlich vage.
3.3 Die Zeichenart betrachten
Die Zeichenart ist ebenso wichtig wie die Länge.
Hex-Darstellung
Besteht der String nur aus [0-9a-fA-F], liegt zunächst eine hex-Darstellung nahe.
In diesem Fall gilt: Zeichenzahl ÷ 2 = rohe Byte-Länge.
- 32 hex → 16 bytes
- 40 hex → 20 bytes
- 64 hex → 32 bytes
- 128 hex → 64 bytes
RFC-4648-Base64 / Base64url
Bei + / = liegt zunächst gewöhnliches Base64 nahe.
Bei - _ liegt Base64url nahe.
Da das padding-= mitunter weggelassen wird, gibt es Längenpaare wie 43 / 44 oder 86 / 88, bei denen beide möglich sind.
crypt-typisches radix64
Tauchen . und / auf und ist der String zugleich mit $...$ abgegrenzt, liegt eher ein crypt-typisches alphabet nahe als gewöhnliches Base64.
bcrypt, sha256crypt, sha512crypt, md5crypt, yescrypt, scrypt und Ähnliche verwenden diese Zeichensatzfamilie.
Das wirkt unscheinbar, ist aber ziemlich wirksam.
Wer denkt, „da ist ein . drin, also ist das kaputtes Base64“, übersieht leicht bcrypt und die crypt(3)-Familie.
3.4 Die Länge zählen
Nach der Zeichenart folgt die Länge. Die Überlegung ist einfach.
- Bei hex:
rohe Byte-Länge = Zeichenzahl / 2 - Bei Base64:
Zeichenzahl ≈ 4 × ceil(rohe Byte-Länge / 3)wobei es 0-2 Zeichen kürzer wird, wenn das padding-=weggelassen wird
Auf dieser Stufe grenzt man die Kandidaten ein. Es ist jedoch sicherer, keinen Sprung wie „64 hex gesehen, also steht SHA-256 fest“ zu machen.
3.5 Über den Kontext festlegen
Was am Ende den Ausschlag gibt, ist der Kontext. Hier kommt man der hundertprozentigen Gewissheit nahe.
-
Liegt in
/etc/shadowLinux-password-hash-Formate wie$y$,$6$,$5$,$1$liegen nahe -
Liegt in
.htpasswdApache-typische Formate wie$apr1$,{SHA}, bcrypt liegen nahe -
Liegt in Djangos Konfiguration oder in
auth_user.passwordDjango-Formate wiepbkdf2_sha256$...oderargon2$...liegen nahe -
Liegt in einer Authentifizierungstabelle von Spring Security
{id}-präfixierte Formate wie{bcrypt}...oder{pbkdf2}...liegen nahe -
32-stelliges Hex im Umfeld von SMB-/AD-Integration Ein NT-Hash (MD4-basiert) liegt stark nahe
In der Praxis ist es nicht selten schneller, das Produkt, das Framework oder den Namen der Konfigurationsdatei der Speicherquelle zu betrachten, statt allein den String selbst anzusehen.
4. Häufige Fehleinschätzungen
4.1 64 hex = SHA-256 vorschnell festlegen
Das kommt ziemlich häufig vor. SHA-256 ist natürlich ein starker Kandidat, aber es gibt mehrere Verfahren mit derselben 32-Byte-Ausgabe. SHA3-256, SHA-512/256, BLAKE2s-256 und die Standardausgabe von BLAKE3 haben ebenfalls dieselbe Länge.
Die Länge ist Material, um eine Kandidatenmenge zu bilden, kein Material für eine Festlegung.
4.2 $6$ mit reinem SHA-512 verwechseln
$6$... ist das prefix von sha512crypt.
Das ist keine „hex-digest von SHA-512“, sondern ein password-hash-String, der salt und rounds enthält.
Ebenso gilt:
$5$ist sha256crypt$1$ist md5crypt
Sobald ein prefix vorhanden ist, handelt es sich nicht mehr um „nur einen digest“.
4.3 {SHA} für „irgendein SHA-256 oder SHA-512“ halten
Im Apache-/LDAP-Kontext bedeutet {SHA} nicht vage „irgendetwas aus der SHA-Familie“.
In den meisten Fällen bezeichnet es einen Base64-codierten SHA-1-digest. {SSHA} ist salted SHA-1.
Wer {SHA} allein aufgrund des Erscheinungsbilds vage als „irgendein SHA“ behandelt, bekommt Verifizierungscode und Migrationslogik falsch.
4.4 Passwort-Hash und Content-Hash als dasselbe behandeln
Auch wenn es sich gleichermaßen um „hash-Strings“ handelt, unterscheidet sich der Zweck.
- digest für die Dateiintegritätsprüfung
- digest für API-Signaturen
- hash-/KDF-String für die Passwortspeicherung
Diese drei sehen ähnlich aus, werden aber unterschiedlich behandelt. Besonders password hash enthält oft salt, rounds, memory cost, parallelism und Ähnliches im String, sodass sich das mit dem Ansatz „rohe digests vergleichen“ nicht durchschauen lässt.
4.5 XOFs und variabel lange digests vergessen
SHAKE128 / SHAKE256 sind XOFs, deren Ausgabelänge sich frei wählen lässt. Auch bei BLAKE2 lässt sich die digest length ändern, und BLAKE3 besitzt ebenfalls extendable output.
Das heißt: Die Vermutung „diese Länge, also dieses Verfahren“ geht daneben, wenn man sich zu sehr auf klassische digests fester Länge verlässt.
4.6 „NTLM“ und „NT-Hash“ als dasselbe behandeln
Das ist eine Frage der Terminologie, wirkt sich aber bei Untersuchungen rund um Windows / AD ziemlich stark aus.
- NT-Hash: Ein 16-Byte-Wert, der entsteht, indem das Passwort UTF-16LE-codiert und durch MD4 geschickt wird. Er erscheint als 32-stelliges Hex, etwa
8846f7eaee8fb117ad06bdd830b7586c. Auch spezifikationsgemäß ist er definiert alsNTOWFv1(Passwd, User, UserDom) = MD4(UNICODE(Passwd)). Diese Definition findet sich unverändert im Abschnitt zur NTLM-v1-Authentication von MS-NLMP, auf das Quelle 13 verweist. - NTLM: Der Name des Authentifizierungsprotokolls, das mit diesem Wert als Schlüssel Challenge/Response durchführt. Es ist kein Name für ein String-Format.
In der Praxis ist auch die Bezeichnung „NTLM-Hash“ verständlich, aber in einer Identifizierungstabelle ist es präziser, NT-Hash (MD4-basiert) zu schreiben. Hält man das auseinander, vermischen sich die Formatfrage - „ist dieses 32-stellige Hex MD5 oder ein NT-Hash?“ - und die Protokollfrage - „läuft diese Kommunikation über NTLM oder Kerberos?“ - nicht miteinander.
Zu beachten ist außerdem, dass der NT-Hash kein salt besitzt. Bei gleichem Passwort ergibt sich immer dasselbe 32-stellige Hex, sodass allein die Beobachtung „ein 32-stelliges Hex ohne salt steht in einer AD-typischen Tabelle“ bereits ein Hinweis ist.
5. Prüfreihenfolge für hundertprozentige Gewissheit
Bei Migrationen oder Authentifizierungsintegrationen ist letztlich Gewissheit nötig. Geht man dabei in folgender Reihenfolge vor, passieren seltener Fehler.
5.1 Die Speicherquelle bestimmen
Zuerst legt man fest, woher der String stammt.
- Aus der shadow-Datei von Linux?
- Aus der basic auth von Apache / Nginx?
- Aus LDAP?
- Aus Django / Spring Security?
- Aus der DB einer eigenen Anwendung?
Die Spezifikation der Speicherquelle ist oft aussagekräftiger als der String allein.
5.2 In der offiziellen Dokumentation das „Speicherformat“ nachschlagen
Als Nächstes schlägt man nicht den Algorithmusnamen, sondern das Speicherformat nach.
Django password formatSpring Security password storage formatcrypt(5) sha512crypt formatApache htpasswd password formats
Mit format / storage / encoding als Suchbegriffen findet man das leichter.
5.3 Bei bekanntem Klartext tatsächlich gegen die Kandidatenverfahren abgleichen
Gibt es ein Testkonto oder einen bekannten Klartext, ist es am schnellsten, mit den Kandidatenverfahren tatsächlich zu rechnen und zu vergleichen. Bei password hash muss man dafür salt und rounds aus dem String extrahieren und neu berechnen.
Das Vorgehen folgt bei jedem Verfahren demselben Drei-Schritte-Muster.
- salt und Parameter aus dem String extrahieren
- Für den bekannten Klartext mit demselben salt und denselben Parametern neu berechnen
- Prüfen, ob der entstehende String exakt mit dem Original übereinstimmt
Einen digest ohne prefix überprüfen
Zuerst geht es um einen „reinen digest“. Dafür reicht die Standardbibliothek von Python 3 aus.
# Python 3.8 oder neuer / nur Standardbibliothek
import base64
import hashlib
target = "5f4dcc3b5aa765d61d8327deb882cf99" # zu identifizierender String
plain = b"password" # bekannter Klartext
for name in ("md5", "sha1", "sha256", "sha512", "sha3_256", "blake2s"):
digest = hashlib.new(name, plain).digest()
if digest.hex() == target.lower():
print("hex-Treffer:", name)
if base64.b64encode(digest).decode() == target:
print("base64-Treffer:", name)
In diesem Beispiel erscheint hex-Treffer: md5. Die Grundform besteht darin, die in der Tabelle von 2.2 genannten Kandidaten unverändert in die for-Schleife einzutragen.
Möchte man den NT-Hash ausprobieren, schreibt man etwa hashlib.new("md4", "password".encode("utf-16-le")). Bei einem Python, das gegen OpenSSL 3.x gelinkt ist, ist der legacy provider jedoch standardmäßig deaktiviert, sodass unsupported hash type md4 auftreten kann. Prüfen Sie das vorher in Ihrer eigenen Umgebung.
crypt-typische Passwort-Hashes überprüfen
$1$ / $5$ / $6$ / $apr1$ lassen sich neu berechnen, indem man openssl passwd das salt übergibt. Auch die in der Tabelle von 2.1 gezeigten Beispiele lassen sich auf diese Weise reproduzieren.
# OpenSSL 3.x
openssl passwd -6 -salt N3v8Kx2Lq9Rt password
# $6$N3v8Kx2Lq9Rt$6LUcSUAELX3aC/.60pTB.TFLTQi1mOGRCwKqNCqtRSaXjorxj01HJ9oNni97Kci1uDt7a/Kn4t3OS20Dw/.vi1
openssl passwd -5 -salt N3v8Kx2Lq9Rt password # sha256crypt
openssl passwd -1 -salt vA7mQ9xZ password # md5crypt
openssl passwd -apr1 -salt vA7mQ9xZ password # Apache APR1-MD5
Stimmt die Ausgabe mit dem zu identifizierenden String überein, stehen an diesem Punkt sowohl das Verfahren als auch der Klartext fest.
Bei einem String mit rounds= muss dieser Wert mit übergeben werden. $6$rounds=5000$... ist der Standardwert, sodass sich mit oder ohne explizite Angabe derselbe digest ergibt - bei einem vom Standard abweichenden Wert wie rounds=100000 müssen Sie jedoch unbedingt mit diesem Wert rechnen.
Unter Debian / Ubuntu geht dasselbe auch mit mkpasswd aus dem Paket whois (in der Form mkpasswd -m sha512crypt -S N3v8Kx2Lq9Rt password).
bcrypt oder Argon2 überprüfen
Bei bcrypt und Argon2 ist das salt in einem eigenen alphabet codiert, daher ist es zuverlässiger, den String als Ganzes an die verify-Funktion einer Bibliothek zu übergeben, statt ihn von Hand herauszuschneiden.
# pip install "passlib[bcrypt]" argon2-cffi
from passlib.hash import argon2, bcrypt
samples = [
(bcrypt, "$2b$12$9YQ2u/e5Y/ArOnG.gJKxK.0makLATcYLP1q.Nsabzrw7XErYCfoYO"),
(argon2, "$argon2id$v=19$m=65536,t=3,p=4$MDEyMzQ1Njc4OWFiY2RlZg$uKZLaN6muIyoyIYr5waqw3y+zaDbe9aLSPj6Ln/rbz4"),
]
for handler, stored in samples:
# identify gibt zurück, ob es dieses Format ist, verify gibt zurück, ob es mit dem Klartext übereinstimmt
print(handler.name, handler.identify(stored), handler.verify("password", stored))
verify liest cost, rounds und salt aus dem String selbst aus und rechnet damit neu, sodass Sie das Extrahieren der Parameter nicht selbst schreiben müssen. Im obigen bcrypt-Beispiel lautet der Klartext password, daher wird True zurückgegeben.
Zu beachten ist außerdem, dass das Python-Standardmodul crypt als deprecated markiert und in Python 3.13 entfernt wurde. Wenn Sie ab 3.13 mit crypt-typischen Formaten arbeiten, weichen Sie auf openssl passwd oder passlib aus.
5.4 Kandidatenverfahren in hashcat-Nummern oder john-Namen übersetzen
Ist der Klartext unbekannt und man möchte das Tool selbst arbeiten lassen, muss man nicht den Verfahrensnamen, sondern den vom Tool verwendeten Identifikator angeben. Hier hakt es häufig, daher eine Tabelle mit den gängigsten Fällen.
| Erscheinungsbild | Verfahren | hashcat-Modus (-m) |
|---|---|---|
| 32 hex | MD5 | 0 |
| 32 hex (aus AD) | NT-Hash | 1000 |
| 40 hex | SHA-1 | 100 |
| 64 hex | SHA-256 | 1400 |
| 128 hex | SHA-512 | 1700 |
$1$... |
md5crypt | 500 |
$apr1$... |
Apache APR1-MD5 | 1600 |
$2a$ / $2b$ / $2y$ |
bcrypt | 3200 |
$5$... |
sha256crypt | 7400 |
$6$... |
sha512crypt | 1800 |
pbkdf2_sha256$... |
Django PBKDF2-HMAC-SHA256 | 10000 |
| scrypt-typisches Speicherformat | scrypt | 8900 |
Die Modusnummern nehmen mit jeder neuen Version zu, prüfen Sie deshalb bei neueren Verfahren wie Argon2 oder yescrypt die Liste, die in der Hilfe Ihres lokalen hashcat erscheint.
Bei John the Ripper gibt man nicht eine Nummer, sondern einen Namen an. Im Kernprogramm (1.8.0) sind descrypt, bsdicrypt, md5crypt, bcrypt, LM, AFS, tripcode, dummy und crypt verfügbar; viele weitere werden erst in der jumbo-Version ergänzt. Fehlt der Name des gewünschten Verfahrens im Kernprogramm, prüfen Sie die Dokumentation der jumbo-Version.
Zur Wiederholung: Verwenden Sie die hier genannten Tools ausschließlich für Ihre eigene Umgebung oder eine Umgebung, für die eine Untersuchungserlaubnis vorliegt.
5.5 Implementierungscode oder Konfiguration prüfen
Handelt es sich beim Untersuchungsziel um ein eigenes System, ist es letztlich am zuverlässigsten, Code und Konfiguration anzusehen.
- Die verwendete Bibliothek
- Die Framework-Konfiguration
- Die zur Erzeugungszeit genutzten Optionen
- Das Ausgabe-Encoding (hex / Base64 / Base64url / crypt-alphabet)
Ein Blick hierauf klärt die Sache meist ab.
5.6 Für die Zukunft mit Verfahrenslabel speichern
Wer künftig selbst ein Design festlegt, macht spätere Migrationen deutlich leichter, wenn er ein Format wählt, das das Verfahren im String einbettet.
- Argon2s PHC string format
- Spring Securitys
{id}encodedPassword - Djangos
algo$iterations$salt$hash - Unix-
crypt(3)-typische Formate mit prefix
So bleibt es für spätere Betrachter deutlich klarer. Ein Design, das dagegen nur „reines 64-stelliges Hex“ in der DB ablegt, ist zum künftigen eigenen Ich wenig freundlich.
6. Zusammenfassung
Beim Bestimmen des Verfahrens hinter der String-Darstellung eines Hash-Werts hilft es, in dieser Reihenfolge vorzugehen.
- Gibt es ein prefix?
- Welche Trennzeichen gibt es?
- Welcher Zeichensatz liegt vor?
- Wie vielen Byte entspricht die Länge?
- Wie lautet der Kontext der Speicherquelle?
Am wichtigsten sind diese zwei Punkte.
- Speicherformate mit Präfix lassen sich recht gut bestimmen
- Reines Hex / Base64 bringt oft nur bis zur Kandidatenmenge
Die praktische Entscheidung sieht daher so aus.
- Bei
$argon2id$...,$2b$...,$6$...,{SHA}...,pbkdf2_sha256$...kommt man allein mit dem String schon ziemlich weit - Bei nur 32-, 40-, 64- oder 128-stelligem Hex denkt man „Kandidaten eingrenzen“, nicht „festlegen“
- Wenn wirklich Gewissheit nötig ist, sieht man sich Produkt, Konfiguration und Implementierung der Speicherquelle an
Geht man in dieser Reihenfolge vor, wird die Untersuchung deutlich schneller. Ein Schnellschluss allein anhand der Länge führt dagegen still auf einen Umweg.
7. Zu diesem Thema passende Leistungen
Technische Beratung und Design-Review
Bei der Bestimmung des Verfahrens hinter Passwort-Hashes in einer bestehenden DB, der Migration einer Authentifizierungsplattform oder der Log-Untersuchung in gemischten Windows-/Web-Systemen reicht es nicht, nur das Erscheinungsbild des Strings zu betrachten - auch die Implementierung der Speicherquelle und die Migrationsstrategie müssen geklärt werden. Betrachtet man Verfahrensbestimmung und Migrationsdesign zusammen, lassen sich Fehler leichter vermeiden.
Fehleruntersuchung und Ursachenanalyse
Untersuchungen, bei denen „die Verifizierung nicht vorankommt, weil unklar ist, was dieser String ist“, sind nicht selten. Grenzt man ein, wo - in Logs, Konfigurationsdateien, DB-Schema oder Anwendungsimplementierung - das Verfahren tatsächlich festgelegt wird, beschleunigt das die Ursachenermittlung erheblich.
8. Quellen
- RFC 1321 - The MD5 Message-Digest Algorithm
- NIST FIPS 180-4 - Secure Hash Standard (SHA-1, SHA-2, SHA-512/224, SHA-512/256)
- NIST FIPS 202 - SHA-3 Standard: Permutation-Based Hash and Extendable-Output Functions
- PHC string format specification
- Argon2 reference implementation
- RFC 7693 - The BLAKE2 Cryptographic Hash and Message Authentication Code (MAC)
- BLAKE3 C README - default output length and extendable output
- crypt(5) - prefixes and hashed passphrase formats
- Apache HTTP Server 2.4 - Password Formats
- slappasswd(8) - RFC 2307 schemes such as {SHA} and {SSHA}
- Django documentation - example of
pbkdf2_sha256$... - Spring Security -
DelegatingPasswordEncoderstorage format{id}encodedPassword - MS-NLMP: NTLM v1 Authentication -
NTOWFv1(Passwd, User, UserDom) = MD4(UNICODE(Passwd)) - hashcat wiki - example hashes (Liste der Modusnummern)
- John the Ripper - command line options (die mit
--format=NAMEverwendbaren Namen) - passlib -
PasswordHashAPI (identifyundverify) - openssl-passwd(1) -
-1/-apr1/-5/-6und-salt
Verwandte Artikel
Aktuelle Artikel mit denselben Schlagwörtern führen zu verwandten Themen weiter.
Windows-Sicherheitsüberwachungsrichtlinien und Ereignisprotokolluntersuchung in der Praxis — Zur IT-Abteilung werden, die Ereignis 4625 lesen kann
Ein praktischer Leitfaden, um auf „Bitte prüfen Sie die Protokolle fehlgeschlagener Anmeldungen“ reagieren zu können. Er behandelt das Ve...
Windows LAPS in der Praxis — Schluss mit dem für alle PCs gleichen lokalen Administratorkennwort
Ein für alle PCs gleiches lokales Administratorkennwort ist der Nährboden für Pass-the-Hash-Angriffe, bei denen die Kompromittierung eine...
Windows-Zertifikatspeicher in der Praxis — Benutzer oder Computer, wofür entscheiden Sie sich?
Sollte ein Client-Zertifikat in den Benutzer- oder den Computerspeicher? Dieser praxisnahe Leitfaden arbeitet die klassischen Zertifikats...
Windows-Firewall und Business-Anwendungen — Eingehende Regeln über das Installationsprogramm registrieren
„Auf dem Entwicklungsrechner funktioniert es, aber beim Kunden gibt es keine Verbindung“ hat fast immer dieselbe Ursache: die Windows-Fir...
Windows Update-Verwaltung nach der WSUS-Abschaffung — Wie Sie zwischen WUfB, Autopatch und Intune wählen
Im September 2024 wurde die Abschaffung von WSUS angekündigt. Das bedeutet keinen sofortigen Stopp, aber die Entwicklung neuer Funktionen...
Verwandte Themen
Diese Seiten ordnen den Artikel in einen größeren Leistungs- und Entscheidungskontext ein.
Technische Windows-Themen
Portal zu Windows-Entwicklung, Fehleranalyse und der Nutzung bestehender Assets.
Leistungen zu diesem Thema
Dieser Artikel ist direkt mit den folgenden Leistungen verbunden.
Technische Beratung und Design-Review
Weil es sich eignet, um Logs, Datenbanken, Authentifizierungsverfahren und Speicherformate bestehender Systeme zu untersuchen, das Hash-Verfahren zu identifizieren und Entscheidungen für Migration oder Untersuchung zu strukturieren.
Häufige Fragen
Fragen, die in Beratungen zu diesem Artikelthema häufig gestellt werden.
- In welcher Reihenfolge sollte ich vorgehen, um das Verfahren hinter einem Hash-String zu bestimmen?
- Es hilft, in der Reihenfolge prefix → Trennzeichen → Zeichenart → Länge → Kontext vorzugehen. Formate, bei denen die ersten Zeichen bereits eine Bedeutung tragen, lassen sich gut identifizieren: Bei $argon2id$ liegt Argon2 nahe, bei $2b$ bcrypt, bei $5$ sha256crypt und bei $6$ sha512crypt. Umgekehrt erlauben reines Hex oder Base64 ohne prefix nur eine Eingrenzung der Kandidaten, sodass man letztlich das Produkt, das Framework oder die Konfiguration der Speicherquelle prüfen muss, um sich festzulegen.
- Darf man einen 64-stelligen Hex-String einfach als SHA-256 festlegen?
- Eine solche Festlegung ist riskant. SHA-256 ist ein starker Kandidat, aber SHA3-256, SHA-512/256, BLAKE2s-256 und die Standardausgabe von BLAKE3 erzeugen dieselbe 32-Byte-Ausgabe, sodass sich das allein über die Länge nicht unterscheiden lässt. Die Länge liefert Material, um eine Kandidatenmenge zu bilden, nicht um sich festzulegen. Wenn Gewissheit nötig ist, muss man bis zur Spezifikation der Speicherquelle, dem in der offiziellen Dokumentation genannten Speicherformat, dem Abgleich mit einem bekannten Klartext und der Prüfung von Implementierungscode oder Konfiguration vordringen.
- Ist ein String, der mit $6$ beginnt, ein SHA-512-Hash?
- Nein. $6$ ist das prefix des Unix-Passwortspeicherformats sha512crypt und keine reine SHA-512-hex-digest, sondern ein Passwort-Hash-String, der salt und rounds enthält. Ebenso ist $5$ sha256crypt und $1$ md5crypt. Sobald ein prefix vorhanden ist, handelt es sich nicht mehr um „nur einen digest“ - wer das verwechselt, bekommt bei der Implementierung von Migrations- oder Verifizierungscode Abweichungen.
- Was lässt sich aus der Zeichenart ablesen?
- Die Zeichenart ist ein ebenso wichtiger Hinweis wie die Länge. Besteht der String nur aus Zeichen im Bereich 0-9a-fA-F, handelt es sich um eine hex-Darstellung, bei der die halbe Zeichenzahl der rohen Byte-Länge entspricht. Bei + / = liegt RFC-4648-Base64 nahe, bei - oder _ Base64url. Tauchen Punkt und Schrägstrich zusammen mit $-Trennzeichen auf, ist es naheliegender, statt gewöhnlichem Base64 ein crypt-typisches alphabet wie bei bcrypt oder sha512crypt zu vermuten. Wer einen Punkt als kaputtes Base64 deutet, übersieht crypt-Formate.
Autorenprofil
Profilseite des Artikelautors.
Go Komura
Geschäftsführer von KomuraSoft LLC
Spezialisiert auf Windows-Softwareentwicklung, technische Beratung und Fehleranalyse, insbesondere bei bestehenden Systemen und schwer reproduzierbaren Störungen.