Benutzerdefinierte John the Ripper Formate erstellen

Kali LinuxBeginner
Jetzt üben

Einleitung

John the Ripper (JtR) ist ein leistungsstarkes und beliebtes Open-Source-Tool zum Knacken von Passwörtern. Eine seiner Hauptstärken ist seine Erweiterbarkeit. Obwohl es standardmäßig eine riesige Anzahl von Hash-Typen unterstützt, können Sie gelegentlich auf ein benutzerdefiniertes oder obskures Hashing-Schema stoßen, das JtR nicht erkennt. In solchen Fällen können Sie Ihr eigenes benutzerdefiniertes Format erstellen, um JtR beizubringen, wie es geknackt werden kann.

In diesem Lab lernen Sie die grundlegenden Konzepte hinter der Erstellung eines benutzerdefinierten JtR-Formats kennen. Dieses Lab ist konzeptionell und konzentriert sich auf die Struktur und den Prozess, um Ihnen das grundlegende Wissen zu vermitteln, das für die Entwicklung voll funktionsfähiger Formate erforderlich ist. Sie werden kein voll funktionsfähiges Format schreiben und kompilieren, aber Sie werden alle notwendigen Schritte verstehen.

Struktur von John the Ripper Formaten verstehen

In diesem Schritt lernen Sie die grundlegende Struktur eines John the Ripper Formats kennen. Das Verständnis dieser Struktur ist der erste und wichtigste Teil bei der Erstellung eines benutzerdefinierten Formats.

JtR-Formate werden in C-Quelldateien (.c) definiert und sind um eine zentrale C-Struktur namens struct fmt_main aufgebaut. Diese Struktur fungiert als Bauplan und teilt JtR alles mit, was es über den Hash-Typ wissen muss.

Die wichtigsten Felder innerhalb von struct fmt_main sind:

  • fmt_label: Eine kurze, eindeutige Zeichenkette, die Sie verwenden, um das Format mit der Kommandozeilenoption --format= anzugeben (z. B. md5raw).
  • fmt_tag: Eine eindeutige Kennung, die intern von JtR verwendet wird, um das Format in gespeicherten Sitzungsdateien zu unterscheiden (z. B. $dynamic_0$).
  • algorithm_name: Eine Zeichenkette, die den Hashing-Algorithmus und seine Eigenschaften beschreibt und zu Informationszwecken verwendet wird.
  • plaintext_length: Die maximale Passwortlänge, die das Format verarbeiten kann.
  • binary_size: Die Größe des rohen Binär-Hashes. Für MD5 wären dies 16 Bytes.
  • salt_size: Die Größe des Salts, falls der Hash-Typ einen verwendet.
  • methods: Dies ist eine verschachtelte Struktur, die Zeiger auf verschiedene Funktionen enthält, die die Logik des Formats implementieren. Die wichtigsten Funktionen sind:
    • valid(): Prüft, ob eine gegebene Hash-Zeichenkette aus einer Eingabedatei für dieses Format gültig ist. Sie ist die erste Kontrollinstanz.
    • split(): Wenn eine Hash-Zeichenkette mehr als nur den Hash enthält (z. B. einen Benutzernamen), trennt diese Funktion die Komponenten.
    • binary(): Konvertiert die hexadezimale oder Base64-Hash-Zeichenkette in ihre rohe Binärdarstellung.
    • salt(): Extrahiert das Salt aus der Hash-Zeichenkette.
    • crypt_all(): Die Kernfunktion. Sie nimmt eine Reihe von Kandidaten-Passwörtern entgegen, hasht sie und bereitet sie für den Vergleich vor.
    • cmp_all(): Vergleicht die neu berechneten Hashes aus crypt_all() mit dem Ziel-Hash, um festzustellen, ob eine Übereinstimmung vorliegt.

Dieser Schritt ist rein theoretisch. Sie müssen keine Befehle ausführen. Im nächsten Schritt werden wir eine reale Formatdatei untersuchen, um diese Struktur in Aktion zu sehen.

Vorhandene Formatdefinitionen identifizieren

In diesem Schritt lokalisieren Sie den Quellcode für vorhandene Formate im John the Ripper Quellcode-Baum. Die Untersuchung dieser Dateien ist der beste Weg, um zu lernen, wie Sie Ihre eigenen erstellen.

Das Setup-Skript für dieses Lab hat den JtR-Quellcode bereits in das Verzeichnis ~/project/john geklont.

Navigieren Sie zuerst in das Verzeichnis src, in dem sich die Quelldateien befinden.

cd ~/project/john/src

Listen Sie nun alle Dateien auf, die ein Format definieren. Konventionsgemäß enden diese Dateien mit _fmt_plug.c.

ls *_fmt_plug.c

Sie sehen eine lange Liste von Dateien, die jeweils einem anderen Hash-Typ entsprechen, den JtR unterstützt.

7z_fmt_plug.c             des_fmt_plug.c          lotus5_fmt_plug.c         ...
afs_fmt_plug.c            django_fmt_plug.c       mdc2_fmt_plug.c
aix_smd5_fmt_plug.c       dmg_fmt_plug.c          md4_fmt_plug.c
aix_ssha_fmt_plug.c       dominosec_fmt_plug.c    md5_fmt_plug.c
...und viele mehr...

Betrachten wir eine relativ einfache Datei, md5_fmt_plug.c, um die struct fmt_main zu sehen, die wir besprochen haben. Wir verwenden cat in Kombination mit head, um nur den oberen Teil der Datei anzuzeigen.

cat md5_fmt_plug.c | head -n 50

In der Ausgabe können Sie die Definition von struct fmt_main fmt_MD5 mit ihrem Label, Tag, Algorithmusnamen und Methodenzeigern sehen, genau wie wir es im vorherigen Schritt beschrieben haben.

Erstellen eines einfachen benutzerdefinierten Formats (Konzeptionell)

In diesem Schritt erstellen wir konzeptionell eine neue C-Quelldatei für ein einfaches benutzerdefiniertes Format. Diese Übung veranschaulicht, wie Sie eine neue Formatdatei strukturieren würden.

Unser Ziel ist es, ein Format zu erstellen, das Hashes mit einem speziellen Präfix erkennt: labex_md5$. Zum Beispiel ein Hash wie labex_md5$87e4e494b2399b0921d44e03693518f9.

Stellen Sie sicher, dass Sie sich immer noch im Verzeichnis ~/project/john/src befinden. Wir verwenden den Texteditor nano, um unsere neue Datei zu erstellen.

nano labex_md5_fmt_plug.c

Kopieren Sie nun den folgenden C-Code und fügen Sie ihn in den nano-Editor ein. Dies ist ein vereinfachtes Grundgerüst und kein voll funktionsfähiges Format, aber es demonstriert die Kernkomponenten.

#if FMT_EXTERNS_H
extern struct fmt_main fmt_labex_md5;
#elif FMT_REGISTERS_H
john_register_one(&fmt_labex_md5);
#else

#include <string.h>
#include "arch.h"
#include "common.h"
#include "formats.h"

#define FORMAT_LABEL        "labex-md5"
#define FORMAT_NAME         "LabEx Custom MD5"
#define ALGORITHM_NAME      "MD5 32/64"
#define PLAINTEXT_LENGTH    32
#define BINARY_SIZE         16
#define SALT_SIZE           0
#define TAG_PREFIX          "labex_md5$"
#define TAG_LENGTH          (sizeof(TAG_PREFIX) - 1)

// Diese Funktion prüft, ob eine Ciphertext-Zeichenkette für unser Format gültig ist
static int valid(char *ciphertext, struct fmt_main *self)
{
    if (strncmp(ciphertext, TAG_PREFIX, TAG_LENGTH))
        return 0;
    char *p = ciphertext + TAG_LENGTH;
    if (hexlenu(p, 0) != 32)
        return 0;
    return 1;
}

struct fmt_main fmt_labex_md5 = {
    {
        FORMAT_LABEL,
        FORMAT_NAME,
        ALGORITHM_NAME,
        BENCHMARK_COMMENT,
        BENCHMARK_LENGTH,
        0,
        PLAINTEXT_LENGTH,
        BINARY_SIZE,
        BINARY_ALIGN,
        SALT_SIZE,
        SALT_ALIGN,
        MIN_KEYS_PER_CRYPT,
        MAX_KEYS_PER_CRYPT,
        FMT_CASE | FMT_8_BIT,
        { NULL },
        { TAG_PREFIX },
        NULL
    }, {
        /* init */      init,
        /* done */      done,
        /* reset */     reset,
        /* prepare */   prepare,
        /* valid */     valid,
        /* split */     split,
        /* binary */    binary,
        /* salt */      salt,
        { NULL },
        /* source */    source,
        {
            /* get_hash* */ get_hash_0,
            /* get_hash* */ get_hash_1,
            /* get_hash* */ get_hash_2,
            /* get_hash* */ get_hash_3,
            /* get_hash* */ get_hash_4,
            /* get_hash* */ get_hash_5,
            /* get_hash* */ get_hash_6
        },
        /* cmp_all */   cmp_all,
        /* cmp_one */   cmp_one,
        /* cmp_exact */ cmp_exact
    }
};

#endif

Nachdem Sie den Code eingefügt haben, speichern Sie die Datei und beenden Sie nano, indem Sie Strg+X, dann Y und dann Enter drücken.

Sie haben nun die Quelldatei für ein neues Format erstellt. Der wichtigste Teil ist die Funktion valid(), die einfach nach unserem Präfix labex_md5$ sucht und sicherstellt, dass der folgende Hash 32 Zeichen lang ist.

John the Ripper mit neuem Format kompilieren (Konzeptionell)

Nachdem Sie nun eine konzeptionelle Formatdatei haben, erklärt dieser Schritt, wie Sie diese in den John the Ripper Build-Prozess integrieren und kompilieren.

Stellen Sie zunächst sicher, dass Sie sich im Verzeichnis src befinden.

cd ~/project/john/src

Der erste Schritt beim Kompilieren von JtR ist das Ausführen des configure-Skripts. Dieses Skript prüft Ihr System auf erforderliche Bibliotheken und richtet die Build-Umgebung ein.

./configure

Nach Abschluss der Konfiguration können Sie den Quellcode mit dem Befehl make kompilieren. Wir verwenden make clean, um frühere Builds zu entfernen, und make -sj4, um die Kompilierung mit 4 parallelen Jobs auszuführen, was den Prozess beschleunigt.

make -s clean && make -sj4

Die Kompilierung dauert ein bis zwei Minuten.

Wichtiger Hinweis: In einem realen Szenario reicht es nicht aus, nur die .c-Datei zu erstellen. Sie müssten auch eine Konfigurationsdatei (wie Makefile.in) bearbeiten, um dem Build-System mitzuteilen, dass Ihre neue Objektdatei labex_md5_fmt_plug.o beim Linken der endgültigen john-Executable einbezogen werden soll. Wir überspringen diese Änderung der Einfachheit halber in diesem Lab. Daher wird die Kompilierung erfolgreich sein, aber unser neues Format wird nicht tatsächlich in die endgültige Binärdatei aufgenommen. Dies veranschaulicht einen kritischen Schritt im Entwicklungsprozess.

Benutzerdefiniertes Format testen (Konzeptionell)

In diesem letzten Schritt besprechen wir, wie Sie Ihr benutzerdefiniertes Format mit der neu kompilierten John the Ripper-Executable testen würden.

Die kompilierte john-Binärdatei befindet sich im Verzeichnis ~/project/john/run/. Navigieren wir dorthin.

cd ~/project/john/run

Sie können alle Formate auflisten, die Ihre kompilierte Version von JtR unterstützt.

./john --list=formats

Scrollen Sie durch die Liste. Sie werden feststellen, dass unser labex-md5-Format nicht vorhanden ist. Dies ist zu erwarten, da wir, wie im vorherigen Schritt erwähnt, die Build-Dateien nicht geändert haben, um es einzubeziehen.

Erstellen wir nun eine Beispiel-Hash-Datei und eine Wortliste, um zu sehen, wie wir das Format verwenden würden, wenn es erfolgreich kompiliert worden wäre.

Erstellen Sie zuerst eine Datei namens hashes.txt im Stammverzeichnis Ihres Projekts, die einen Hash enthält, der unserem benutzerdefinierten Format entspricht. Der Hash 87e4e494b2399b0921d44e03693518f9 ist der MD5-Hash des Passworts "labex".

echo "labex_md5$87e4e494b2399b0921d44e03693518f9" > ~/project/hashes.txt

Erstellen Sie als Nächstes eine einfache Wortliste, die das richtige Passwort enthält.

echo "labex" > ~/project/wordlist.txt

Schließlich ist dies der Befehl, den Sie ausführen würden, um den Hash mit dem benutzerdefinierten Format zu knacken.

./john --format=labex-md5 --wordlist=~/project/wordlist.txt ~/project/hashes.txt

Wenn Sie diesen Befehl ausführen, meldet JtR einen Fehler, da das Format unbekannt ist, was unser Verständnis des Kompilierungsprozesses bestätigt.

Unknown format name: "labex-md5"

Damit ist unser konzeptioneller Überblick über die Erstellung und das Testen eines benutzerdefinierten JtR-Formats abgeschlossen.

Zusammenfassung

In diesem Lab haben Sie den konzeptionellen Prozess der Erstellung benutzerdefinierter Formate für John the Ripper erkundet. Sie haben ein grundlegendes Verständnis der wichtigsten Schritte zur Erweiterung der Fähigkeiten von JtR gewonnen.

Sie haben gelernt über:

  • Die Kernstruktur struct fmt_main, die jedes JtR-Format definiert.
  • Wie man bestehende Formatdefinitionen im JtR-Quellcode findet und untersucht.
  • Den Prozess der Erstellung einer neuen Formatquelldatei mit einer grundlegenden valid()-Funktion.
  • Das Standardverfahren zum Kompilieren von JtR mit ./configure und make.
  • Den kritischen (und absichtlich ausgelassenen) Schritt der Modifizierung von Build-Dateien zur Einbeziehung eines neuen Formats.
  • Wie man ein Format mit dem --format-Flag und einer Beispiel-Hash-Datei testet.

Obwohl dieses Lab nicht zu einem voll funktionsfähigen benutzerdefinierten Format geführt hat, hat es Ihnen das wesentliche Wissen und eine klare Roadmap für die Entwicklung Ihrer eigenen Formate zur Bewältigung einzigartiger Passwort-Hashing-Schemata vermittelt.