はじめに
John the Ripper (JtR) は、強力で人気のあるオープンソースのパスワードクラッキングツールです。その主な強みの一つは拡張性です。JtR は標準で膨大な数のハッシュタイプをサポートしていますが、JtR が認識しないカスタムまたはマイナーなハッシュスキームに遭遇することがあります。そのような場合、JtR にそれをクラックする方法を教えるために独自のカスタムフォーマットを作成できます。
この実験 (lab) では、カスタム JtR フォーマットを作成する際の基本的な概念を学びます。この実験は概念的なものであり、構造とプロセスに焦点を当てており、完全に機能するフォーマットを開発するために必要な基礎知識を提供します。完全に動作するフォーマットを作成してコンパイルするわけではありませんが、必要なすべての手順を理解できます。
John the Ripper フォーマット構造の理解
このステップでは、John the Ripper フォーマットの基本的な構造について学びます。この構造を理解することは、カスタムフォーマットを作成する上で最初かつ最も重要な部分です。
JtR フォーマットは C ソースファイル (.c) で定義され、struct fmt_main という名前の中核となる C 構造体を中心に構築されています。この構造体は設計図として機能し、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(): 16 進数または 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
...and many more...
比較的簡単な md5_fmt_plug.c を見て、前述の struct fmt_main を確認しましょう。ファイルの先頭部分のみを表示するために、cat と head を組み合わせて使用します。
cat md5_fmt_plug.c | head -n 50
出力では、前のステップで説明したように、ラベル、タグ、アルゴリズム名、メソッドポインタを含む struct fmt_main fmt_MD5 の定義を確認できるはずです。
シンプルなカスタムフォーマットの作成(概念)
このステップでは、シンプルなカスタムフォーマット用の新しい 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)
// 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
コードを貼り付けたら、ファイルを保存して nano を終了します。Ctrl+X、次に Y、最後に Enter を押します。
これで新しいフォーマットのソースファイルが作成されました。最も重要な部分は valid() 関数で、これは単に labex_md5$ プレフィックスをチェックし、それに続くハッシュが 32 文字であることを確認します。
新しいフォーマットで John the Ripper をコンパイルする(概念)
概念的なフォーマットファイルが作成されたので、このステップではそれを John the Ripper のビルドプロセスに統合し、コンパイルする方法を説明します。
まず、src ディレクトリにいることを確認してください。
cd ~/project/john/src
JtR をコンパイルする最初のステップは、configure スクリプトを実行することです。このスクリプトは、システムに必要なライブラリをチェックし、ビルド環境をセットアップします。
./configure
設定が完了したら、make コマンドを使用してソースコードをコンパイルできます。ここでは make clean を使用して以前のビルドを削除し、make -sj4 を使用して 4 つの並列ジョブでコンパイルを実行し、プロセスを高速化します。
make -s clean && make -sj4
コンパイルには 1 分から 2 分かかります。
重要な注意点: 実際のシナリオでは、.c ファイルを作成するだけでは不十分です。ビルドシステムに、最終的な john 実行可能ファイルをリンクする際に新しい labex_md5_fmt_plug.o オブジェクトファイルを含めるように指示するために、設定ファイル(Makefile.in など)を編集する必要もあります。この実験では、簡単にするためにその変更は省略しています。したがって、コンパイルは成功しますが、新しいフォーマットは実際には最終的なバイナリに含まれません。これは開発プロセスにおける重要なステップを示しています。
カスタムフォーマットのテスト(概念)
この最終ステップでは、新しくコンパイルされた John the Ripper 実行可能ファイルでカスタムフォーマットをテストする方法について説明します。
コンパイルされた john バイナリは、~/project/john/run/ ディレクトリにあります。そこへ移動しましょう。
cd ~/project/john/run
コンパイルされた JtR のバージョンがサポートするすべてのフォーマットを一覧表示できます。
./john --list=formats
リストをスクロールしてください。labex-md5 フォーマットが存在しないことに気づくでしょう。これは予想通りです。前のステップで述べたように、フォーマットを含めるようにビルドファイルを変更しなかったためです。
次に、フォーマットが正常にコンパイルされた場合にどのように使用するかを確認するために、サンプルハッシュファイルと単語リストを作成しましょう。
まず、プロジェクトのルートディレクトリに hashes.txt という名前のファイルを作成し、カスタムフォーマットに一致するハッシュを含めます。ハッシュ 87e4e494b2399b0921d44e03693518f9 は、パスワード "labex" の MD5 ハッシュです。
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 の機能を拡張する上で含まれる主要なステップについての基本的な理解を得ました。
以下の点について学びました。
- すべての JtR フォーマットを定義するコアとなる
struct fmt_main構造体。 - JtR ソースコード内の既存のフォーマット定義を見つけ、調べる方法。
- 基本的な
valid()関数を持つ新しいフォーマットソースファイルを作成するプロセス。 ./configureおよびmakeを使用して JtR をコンパイルする標準的な手順。- 新しいフォーマットを含めるためにビルドファイルを変更するという、重要(かつ意図的に省略された)ステップ。
--formatフラグとサンプルハッシュファイルを使用してフォーマットをテストする方法。
この実験では完全に機能するカスタムフォーマットを作成しませんでしたが、独自のフォーマットを開発して独自のパスワードハッシュスキームに対応するための、不可欠な知識と明確なロードマップを提供しました。


