Введение
John the Ripper (JtR) — это мощный и популярный инструмент с открытым исходным кодом для взлома паролей. Одной из его ключевых особенностей является расширяемость. Хотя он из коробки поддерживает огромное количество типов хешей, вы можете столкнуться с пользовательской или малоизвестной схемой хеширования, которую JtR не распознает. В таких случаях вы можете создать собственный формат, чтобы научить JtR его взламывать.
В этой лабораторной работе вы изучите фундаментальные концепции создания пользовательского формата JtR. Эта лабораторная работа носит концептуальный характер и фокусируется на структуре и процессе, предоставляя вам базовые знания, необходимые для разработки полностью функциональных форматов. Вы не будете писать и компилировать полностью рабочий формат, но вы поймете все необходимые шаги.
Понимание структуры формата John the Ripper
На этом этапе вы узнаете об основной структуре формата John the Ripper. Понимание этой структуры является первой и самой важной частью создания пользовательского формата.
Форматы JtR определяются в исходных файлах на языке C (.c) и построены вокруг основной структуры C с именем struct fmt_main. Эта структура действует как шаблон, сообщая JtR все, что ему нужно знать о типе хеша.
Ключевые поля в struct fmt_main включают:
fmt_label: Короткая, уникальная строка, которую вы используете для указания формата с помощью опции командной строки--format=(например,md5raw).fmt_tag: Уникальный идентификатор, используемый JtR внутри для различения формата в файлах сохраненных сессий (например,$dynamic_0$).algorithm_name: Строка, описывающая алгоритм хеширования и его свойства, используемая в информационных целях.plaintext_length: Максимальная длина пароля, которую может обрабатывать формат.binary_size: Размер необработанного бинарного хеша. Для MD5 это будет 16 байт.salt_size: Размер соли, если тип хеша ее использует.methods: Это вложенная структура, содержащая указатели на различные функции, реализующие логику формата. Наиболее важные функции:valid(): Проверяет, является ли данная строка хеша из входного файла действительной для этого формата. Это первый фильтр.split(): Если строка хеша содержит больше, чем просто хеш (например, имя пользователя), эта функция разделяет компоненты.binary(): Преобразует шестнадцатеричную или base64 строку хеша в ее необработанное бинарное представление.salt(): Извлекает соль из строки хеша.crypt_all(): Основная функция. Она принимает набор кандидатных паролей, хеширует их и подготавливает для сравнения.cmp_all(): Сравнивает новые хеши, вычисленныеcrypt_all(), с целевым хешем, чтобы определить, есть ли совпадение.
Этот этап является чисто теоретическим. Вам не нужно выполнять никаких команд. На следующем этапе мы рассмотрим реальный файл формата, чтобы увидеть эту структуру в действии.
Определение существующих форматов
На этом этапе вы найдете исходный код существующих форматов в дереве исходного кода John the Ripper. Изучение этих файлов — лучший способ научиться создавать свои собственные.
Скрипт настройки для этой лабораторной работы уже клонировал исходный код JtR в каталог ~/project/john.
Сначала перейдите в каталог src, где расположены исходные файлы.
cd ~/project/john/src
Теперь выведите список всех файлов, которые определяют формат. По соглашению, эти файлы заканчиваются на _fmt_plug.c.
ls *_fmt_plug.c
Вы увидите длинный список файлов, каждый из которых соответствует различному типу хеша, поддерживаемому JtR.
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
...и многие другие...
Давайте рассмотрим относительно простой файл md5_fmt_plug.c, чтобы увидеть описанную нами ранее struct fmt_main. Мы используем cat в сочетании с head, чтобы просмотреть только верхнюю часть файла.
cat md5_fmt_plug.c | head -n 50
В выводе вы сможете увидеть определение struct fmt_main fmt_MD5 с ее меткой (label), тегом (tag), названием алгоритма (algorithm name) и указателями на методы (method pointers), точно так, как мы описали на предыдущем шаге.
Create a Simple Custom Format (Conceptual)
In this step, we will conceptually create a new C source file for a simple custom format. This exercise will illustrate how you would structure a new format file.
Our goal is to create a format that recognizes hashes with a special prefix: labex_md5$. For example, a hash like labex_md5$87e4e494b2399b0921d44e03693518f9.
Ensure you are still in the ~/project/john/src directory. We will use the nano text editor to create our new file.
nano labex_md5_fmt_plug.c
Now, copy and paste the following C code into the nano editor. This is a simplified skeleton and is not a fully functional format, but it demonstrates the core components.
#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)
// This function checks if a hash string is valid for our format
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
After pasting the code, save the file and exit nano by pressing Ctrl+X, then Y, and then Enter.
You have now created the source file for a new format. The most important part is the valid() function, which simply checks for our labex_md5$ prefix and ensures the following hash is 32 characters long.
Создание простого пользовательского формата (концептуально)
На этом этапе мы концептуально создадим новый файл исходного кода на C для простого пользовательского формата. Это упражнение продемонстрирует, как следует структурировать новый файл формата.
Наша цель — создать формат, который распознает хеши со специальным префиксом: labex_md5$. Например, хеш вида labex_md5$87e4e494b2399b0921d44e03693518f9.
Убедитесь, что вы все еще находитесь в каталоге ~/project/john/src. Мы будем использовать текстовый редактор nano для создания нашего нового файла.
nano labex_md5_fmt_plug.c
Теперь скопируйте и вставьте следующий код на C в редактор nano. Это упрощенный каркас, а не полностью функциональный формат, но он демонстрирует основные компоненты.
#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)
// Эта функция проверяет, является ли строка хеша действительной для нашего формата
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
После вставки кода сохраните файл и выйдите из nano, нажав Ctrl+X, затем Y и затем Enter.
Теперь вы создали исходный файл для нового формата. Самая важная часть — это функция valid(), которая просто проверяет наш префикс labex_md5$ и гарантирует, что следующий хеш имеет длину 32 символа.
Тестирование пользовательского формата (концептуально)
На этом заключительном этапе мы обсудим, как вы могли бы протестировать свой пользовательский формат с недавно скомпилированным исполняемым файлом John the Ripper.
Скомпилированный бинарный файл john находится в каталоге ~/project/john/run/. Перейдем туда.
cd ~/project/john/run
Вы можете вывести список всех форматов, которые поддерживает ваша скомпилированная версия JtR.
./john --list=formats
Прокрутите список. Вы заметите, что наш формат labex-md5 отсутствует. Это ожидаемо, потому что, как упоминалось на предыдущем шаге, мы не изменяли файлы сборки, чтобы включить его.
Теперь давайте создадим пример файла хешей и список слов, чтобы увидеть, как мы бы использовали формат, если бы он был успешно скомпилирован.
Сначала создайте файл с именем hashes.txt в корневом каталоге вашего проекта, содержащий хеш, соответствующий нашему пользовательскому формату. Хеш 87e4e494b2399b0921d44e03693518f9 является MD5-хешем пароля "labex".
echo "labex_md5$87e4e494b2399b0921d44e03693518f9" > ~/project/hashes.txt
Затем создайте простой список слов, содержащий правильный пароль.
echo "labex" > ~/project/wordlist.txt
Наконец, вот команда, которую вы бы выполнили для взлома хеша с использованием пользовательского формата.
./john --format=labex-md5 --wordlist=~/project/wordlist.txt ~/project/hashes.txt
При выполнении этой команды JtR сообщит об ошибке, поскольку формат неизвестен, что подтверждает наше понимание процесса компиляции.
Unknown format name: "labex-md5"
На этом завершается наш концептуальный обзор создания и тестирования пользовательского формата JtR.
Итоги
В этой лабораторной работе вы изучили концептуальный процесс создания пользовательских форматов для John the Ripper. Вы получили фундаментальное понимание ключевых шагов, необходимых для расширения возможностей JtR.
Вы узнали о:
- Основной структуре
struct fmt_main, которая определяет каждый формат JtR. - Как находить и изучать существующие определения форматов в исходном коде JtR.
- Процессе создания нового файла исходного кода формата с базовой функцией
valid(). - Стандартной процедуре компиляции JtR с использованием
./configureиmake. - Критическом (и намеренно пропущенном) шаге изменения файлов сборки для включения нового формата.
- Как тестировать формат с помощью флага
--formatи файла с примером хеша.
Хотя эта лабораторная работа не привела к созданию полностью функционального пользовательского формата, она предоставила вам необходимые знания и четкий план для разработки собственных форматов для решения уникальных схем хеширования паролей.


