john (2)

Table of Contents

black-box check

前回は、パスワード・クラッカーの一種であるjohnで脆弱なパスワードの検出を 始めてみた。どうも、近代的なパスワードには、対応出来ていないみたい(未来は 発明できない)。パスワードの例が出てたんだけど、1ユーザー分しかない。 そこで、変形をかけて、増量してみた。

余談になるけど、流行のAI。世界中のデータを取り尽してしまって、もう無いみたい。 そこで、図形とかだと、斜めにするとか、歪ませるとか、鏡像にするとかして増量 してるらしい。パンダなら、白黒を黒白にするんだな。新種のパンダが発見され ました。名前は、ダンパと付けました。だって色合いが逆ですから。。こういう ユーモアは、まだAIには無理かな? 深刻なのは、テキストデータだそうです。 AIが出力したテキストを材料に追加すると、どんどんと性能が劣化していくそう です。

john用に増量したデータです。ユーザー名を全て別にしました。john,barは、パスワード の最初と最後を別の文字に変更しています。

eq$ cat G
user:AZl.zWwxIh15Q
john:BZl.zWwxIh15Q
who:AZl.zWwxIh15Q
bar:AZl.zWwxIh15P

これでクラッキング開始。

eq$ john G
Created directory: /home/sakae/.john
Loaded 3 password hashes with 2 different salts (descrypt, traditional crypt(3) [DES 128/128 SSE2-16])
Press 'q' or Ctrl-C to abort, almost any other key for status
example          (who)
example          (user)
Warning: MaxLen = 13 is too large for the current hash type, reduced to 8
2g 0:00:00:50 3/3 0.03928g/s 3622Kp/s 3623Kc/s 3624KC/s cjb3h1..cjbcue
Use the "--show" option to display all of the cracked passwords reliably
Session aborted

2ユーザー分は直にクラック成功。有効なパスワードは、どうやら3個だけだったようです。 もう1ユーザーのクラックの為に、ひたすら計算してるみたい。途中で中止しました。

log

.johnにログが残っていました。

eq$ tree .john
.john
|-- john.log
|-- john.pot
`-- john.rec

このうちの、john.logが面白いです。

0:00:00:00 Starting a new session
0:00:00:00 Loaded a total of 3 password hashes with 2 different salts
0:00:00:00 - Hash type: descrypt, traditional crypt(3) (lengths up to 8, longer passwords split)
0:00:00:00 - Algorithm: DES 128/128 SSE2-16
0:00:00:00 - Candidate passwords will be buffered and tried in chunks of 128
0:00:00:00 - Configured to use otherwise idle processor cycles only
0:00:00:00 Proceeding with "single crack" mode
0:00:00:00 - 1081 preprocessed word mangling rules
0:00:00:00 - Allocated 2 buffers of 128 candidate passwords each
0:00:00:00 - Rule #1: ':' accepted as ''
0:00:00:00 - Rule #2: '-s x**' rejected
0:00:00:00 - Rule #3: '-c (?a c Q' accepted as '(?acQ'
  :
0:00:00:00 - Rule #1081: 'l Az"1900" <+' accepted as 'lAz"1900"<+'
0:00:00:00 - Processing the remaining buffered candidate passwords, if any
0:00:00:00 Proceeding with wordlist mode
0:00:00:00 - Wordlist file: /mnt/opt/share/john/password.lst
0:00:00:00 - 57 preprocessed word mangling rules
0:00:00:00 - Rule #1: ':' accepted as ''
0:00:00:00 + Cracked who
0:00:00:00 + Cracked user
0:00:00:00 - Rule #2: '-c >3 !?X l Q' accepted as '>3!?XlQ'
0:00:00:00 - Rule #3: '-c (?a >2 !?X c Q' accepted as '(?a>2!?XcQ'
  :
0:00:00:50 - Trying length 5, fixed @5, character count 27
0:00:00:50 - Switching to length 6
0:00:00:50 - Expanding tables for length 6 to character count 26
0:00:00:50 - Trying length 6, fixed @5, character count 21
0:00:00:50 Session aborted

パスワードの形式を最初に検出して、それに見合う戦略を立ててから、探索を してるっぽいです。

gdb break

パスワードの形式をどうやって推定してるんだろう? 新式の奴はさっぱり対応して くれなかったので、このあたりが第一関門になりそう。特徴的なエラー語句に、FAQ なんて文字が含まれていたんで、家捜し。

eq$ cd john-1.8.0/src/
eq$ grep FAQ *.[chS]
john.c:                 printf("No password hashes %s (see FAQ)\n",

これを出しているのは、 john_load って関数内。

(gdb) b john_load
Function "john_load" not defined.
Make breakpoint pending on future shared library load? (y or [n]) y
 :
Created directory: /home/sakae/.john
Loaded 3 password hashes with 2 different salts (descrypt, traditional crypt(3) [DES 128/128 SSE2-16])
Press 'q' or Ctrl-C to abort, almost any other key for status
example          (who)
example          (user)

そんな関数は無いって言われた。実行すると、スルーされたよ。そんじゃ、別の手で、 パスワード数を数える変数が変化したらブレークしてねって指示。

このwatchって機能は、ハード屋さんの御用達。昔ちょっと触ったミニコンに、この 機能が付いていた。コンソールのSWに監視したいアドレスをセット。ロータリーSWで、 モードを書き込み検出にしとく。そうするとメモリー・アドレスがコンソールSWにセットしたアドレスと 一致して、書き込みが発生した時に、モニターモードになる。もう、これだけで回路 を設計できるよ。実際は、この条件だけではおぼつかないんで、命令の実行時って 条件を加えるといいだろう。

amd64なCPU自身にデバッグの為のハード機構が実装されてるはずなんだけど、現gdbでは この機構を利用していない。シュミレーションで実現してるんで、実行がメチャクチャ 遅いんで注意。

eq$ gdb john
GNU gdb 6.3
Copyright 2004 Free Software Foundation, Inc.
  :
(gdb) watch database.password_count
Watchpoint 1: database.password_count
(gdb) r G
Starting program: /mnt/opt/bin/john G
Watchpoint 1: database.password_count
Watchpoint 1: database.password_count
Error while reading shared library symbols:
Dwarf Error: wrong version in compilation unit header (is 4, should be 2) [in module /ram/usr/libexec/ld.so]
Created directory: /home/sakae/.john

先程は気が付かなかったけど、エラーが発生してる。 コンパイラーの出力するデバッグ情報と、gdbが想定 してる情報に齟齬が有るって。デフォで提供されてるgdbは、歴史の彼方の物だから なあ。20年も経てば、gcc -> clang と変わるしねぇ。ここは腹を括って、パッケージ版 のgdbを入れるか。

その前に、一つ解決しておかねばならない事が有る。定義されてる関数がgdbから、 見えない問題。

eq$ nm /mnt/opt/bin/john | grep john_
0007cf00 B john_child_count
0007cf08 B john_child_pids
0007d2e0 b john_home_length
0007d2d8 b john_home_path
0007d0d0 b john_loaded_counts.s_loaded_counts
00049e20 D john_main_process
0002fe00 t john_register_all

登録されていないじゃん。これじゃgdbもお手上げだわな。

static void john_load(void)
{
        struct list_entry *current;

        umask(077);

問題の関数定義は、こうなってました。staticなんて言う甘言に騙されてはいけません。 裏の顔を持っているんです。コンパイル単位(普通はファイル毎)で、プライベートです。 この裏の顔が機能して、シンボルに表われないように抑止したんでしょう。だって名前 が解れば呼び出せますからね。調べてみると、あちこちにstaticが付いてました。

gdb 9.2

パッケージから入れたものは、デフォと名前が衝突しないように、egdbってなってる。

eq$ egdb john
GNU gdb (GDB) 9.2
Copyright (C) 2020 Free Software Foundation, Inc.
 :
(gdb) watch database.password_count
Hardware watchpoint 1: database.password_count
(gdb) r G
Starting program: /mnt/opt/bin/john G
Created directory: /home/sakae/.john

Watchpoint 1: database.password_count

Old value = 0
New value = 1
ldr_load_pw_line (db=0xfdef2dd4f18 <database>, line=<optimized out>)
    at loader.c:577
warning: Source file is more recent than executable.
577                     last_pw = current_salt->list;
(gdb) bt
#0  ldr_load_pw_line (db=0xfdef2dd4f18 <database>, line=<optimized out>)
    at loader.c:577
#1  0x00000fdef2d88529 in read_file (db=0xfdef2dd4f18 <database>,
    name=<optimized out>, flags=<optimized out>,
    process_line=0xfdef2d885c0 <ldr_load_pw_line>) at loader.c:66
#2  0x00000fdef2d874cc in john_load () at john.c:427
#3  john_init (name=<optimized out>, argc=<optimized out>,
    argv=<optimized out>) at john.c:554
#4  main (argc=<optimized out>, argv=<optimized out>) at john.c:702#

大体どのあたりかが、判ってきたな。

Ask Chatgpt

東大・東大生が公開しているエンジニアが読むべき資料まとめ こういうのを見るまでもなく、良い質問を出来る人が賢人だそうだ。

上の例でパスワード・フィールドがどういう構造か、質問してみたぞ。

DESベースのハッシュ (descrypt) の構造
Salt (ソルト)

    長さ: 2文字
    使用文字: a-z, A-Z, 0-9, . (ピリオド), / (スラッシュ)
    ソルトはランダムに生成され、パスワードをハッシュ化する際に追加されます。
    これにより、同じパスワードでも異なるソルトが与えられると異なるハッシュ値が
    生成されます。

    Hash (ハッシュ)
        長さ: 11文字
        ソルトとパスワードを基に、標準的なDES(Data Encryption Standard)を使用
	して計算されます。
        DESは、64ビット(56ビットの鍵長)の暗号アルゴリズムで、計算結果が
	エンコードされます。

したがって、descrypt のハッシュは 13文字(2文字のソルト + 11文字のハッシュ)
から成ります。

制限:
    パスワードの長さは 最大8文字(それ以上は切り捨てられます)。
    使用可能な文字セットは大文字、小文字、数字、.、/ のみ。

今となっては、とても弱いパスワードしか生成できない仕様なんだな。

オイラーは、パスワードファイルをユーザー数で捉えていたけど、john君は、salt+hash の文字列の種類数を主体に考えていたのね。

trace

ようやくgdb出来るようになったので、少し追跡してみる。慣れ親しんだemacs+gdbって便利だなあ。

          count = ldr_split_line(&login, &ciphertext, &gecos, &home,
                  NULL, &db->format, db->options, line);

(gdb) p login
$3 = 0x6fedd82c2960 "user"
(gdb) p ciphertext
$4 = 0x6fedd82c2965 "AZl.zWwxIh15Q"

パスワードファイルの1行を取り出して、分解してる。そして、データベースを 初期化してる。

(gdb) p *db
$6 = {
  loaded = 0,
  options = 0xb7eb6d19b40,
  salts = 0x0,
  salt_hash = 0xb7f3019f000,
  password_hash = 0xb7f1c399000,
  password_hash_func = 0xb7c4c0c4390 <binary_hash_4>,
  cracked_hash = 0x0,
  plaintexts = 0xb7eb6d19b68,
  salt_count = 0,
  password_count = 0,
  guess_count = 0,
  format = 0xb7c4c11d050 <fmt_DES>
}

取り合えずフォーマットはDESに決め打ちみたいだな。 そして、詳細は、 DES_fmt.c あたりを見ろとな。

valid blowfish

なんてのは嘘解析だった。 ldr_split_line() の中に秘密が隠されていた。 この関数の中で、solt+hash の文字列を解析して、この暗号部のフォーマット(DES,MD5,..) を決定してる。

決定に関わるのは、 XXX_fmt.c にそれぞれ定義されている、valid()って関数だ。

eq$ ls *_fmt.c
AFS_fmt.c    BSDI_fmt.c   LM_fmt.c     c3_fmt.c
BF_fmt.c     DES_fmt.c    MD5_fmt.c    trip_fmt.c

8種類のフォーマットに対応してるとな。ひょっとして、BFってblowfishの事? 以前 john --test した時のメッセージを参照。

Benchmarking: bcrypt ("$2a$05", 32 iterations) [Blowfish 32/64 X2]... DONE
Raw:    198 c/s real, 198 c/s virtual

ああ、予想は的中した。これで安心して、valid()関数を調査できるな。

static int valid(char *ciphertext, struct fmt_main *self)
{
        int rounds;
        char *pos;

        if (strncmp(ciphertext, "$2a$", 4) &&
            strncmp(ciphertext, "$2x$", 4) &&
            strncmp(ciphertext, "$2y$", 4))
                return 0;

        if (ciphertext[4] < '0' || ciphertext[4] > '9') return 0;
        if (ciphertext[5] < '0' || ciphertext[5] > '9') return 0;
        if (ciphertext[6] != '$') return 0;
        rounds = atoi(ciphertext + 4);
        if (rounds < 4 || rounds > 31) return 0;

        for (pos = &ciphertext[7]; atoi64[ARCH_INDEX(*pos)] != 0x7F; pos++);
        if (*pos || pos - ciphertext != CIPHERTEXT_LENGTH) return 0;

        if (BF_atoi64[ARCH_INDEX(*(pos - 1))] & 3) return 0;
        if (BF_atoi64[ARCH_INDEX(ciphertext[28])] & 0xF) return 0;

        return 1;
}

検査に失敗すると0点、成功すると1点と言う事で、blowfishと認定されるんだな。 所で、どんなフォーマット? 素晴しい解説が有った。 bcrypt(blowfish)

johnみたいな不埒な野郎に対抗する為、わざと計算に時間が かかる様に設計してあるそうだ。そしてその計算負荷を可変する事が可能とな。 上の監査では、roundsがそれに当たり、4ー31の範囲にしてくれよ、だな。

break blowfish

ここまで判明すれば、前回作成したパスワードも破る事ができるな。但し前回のものは、 現行のOpenBSDで作成したんで、先頭から、$2b\(09\) となっている。johnに適合 するように、$2a\(09\) と変更しておいた。

eq$ cat >BF
joy:$2b$09$j4Z6fdVwOWOMwJQavEfPf.NP0WGPqXms7aPthkFEKJOIC0mko5pOy:1001:1001::0:0:joy:/home/joy:/bin/ksh
eq$ emacs BF   ;; $2b  -> $2a
eq$ john BF
Loaded 1 password hash (bcrypt [Blowfish 32/64 X2])
Press 'q' or Ctrl-C to abort, almost any other key for status
123456           (joy)
1g 0:00:01:04 100% 2/3 0.01546g/s 12.63p/s 12.63c/s 12.63C/s 123456..12345
Use the "--show" option to display all of the cracked passwords reliably
Session completed

他のパスワードだと1秒以内で破れているけど、blowfishだと計算負荷が高い為、 解析完了まで、1分以上かかっている。

そして、その時のログの末尾部分。

eq$ tail .john/john.log
0:00:01:03 - Rule #1079: 'l Az"1902" <+' accepted as 'lAz"1902"<+'
0:00:01:03 - Rule #1080: 'l Az"1901" <+' accepted as 'lAz"1901"<+'
0:00:01:03 - Rule #1081: 'l Az"1900" <+' accepted as 'lAz"1900"<+'
0:00:01:04 - Processing the remaining buffered candidate passwords, if any
0:00:01:04 Proceeding with wordlist mode
0:00:01:04 - Wordlist file: /mnt/opt/share/john/password.lst
0:00:01:04 - 57 preprocessed word mangling rules
0:00:01:04 - Rule #1: ':' accepted as ''
0:00:01:04 + Cracked joy
0:00:01:04 Session completed

続いては、どうやって解析してるんだろーー、を調べるのが筋だと思うんだけど、 それは置いておいて、みんなが寄贈したと言うjohn-jumboを試してみたい。

john-jumbo

パッケージから入れるか? それともソースからか? そんな事で悩むな。Linux野郎 じゃあるまいし。

doc/INSTALLを見ると、 ./configure && make -s clean && make -sj4 ってな事が 書かれている。時間がかかるんで、CPUが手分けしてやる設定だな。少々ビビル。

INSTALL-UBUNTUとかINSTALL-WINDOWSなんてのが有るけど、BSD系は説明無し。 見捨てられてる? いや、説明なんて無くても、どうにかしゃう人達でしょって、好意的 に捉えておこう。それでも、参考に覗いてみると

==== If you have an NVIDIA GPU (OpenCL support)

    sudo apt-get -y install nvidia-opencl-dev

==== If you have an AMD GPU (OpenCL support)
    :

やっぱり出てきたね。GPUを使うって、仮想通貨のマイニング屋さんみたいだ。感心 してる場合じゃないでしょ。

古い人向け、に Makefile.legacy が用意されてた。これを使えとな。古いjohnの経験が ここで生きて来るんだな。と思ったら。makeはgmakeを使えって罠が仕掛けられていた。 BSDmakeだと文法違反だってさ。まあ、典型的なエラーなんで、すぐに気がついたけど。

心配してたこんぱいる時間は気にする程でもなかった。どんな風に工程が進むか、 psで眺めているうちに、終了した。んで、インストールはしないで、その場で テストだな。

test

eq$ cd run
eq$ ./john --test
fopen: /etc/john.conf: No such file or directory
eq$ doas cp john.conf /etc
eq$ ./john --test >/tmp/LOG

興味が有るものを拾い出してみた。

eq$ less /tmp/LOG
Benchmarking: descrypt, traditional crypt(3) [DES 128/128 SSE2]... DONE
Many salts:     4305K c/s real, 4263K c/s virtual
Only one salt:  4116K c/s real, 4116K c/s virtual
  :
Benchmarking: AndroidBackup [PBKDF2-SHA1 128/128 SSE2 4x2 AES]... DONE
Benchmarking: AzureAD [PBKDF2-SHA256 128/128 SSE2 4x]... DONE
Benchmarking: Django (x10000) [PBKDF2-SHA256 128/128 SSE2 4x]... DONE
Benchmarking: IKE, PSK [HMAC MD5/SHA1 32/64]... DONE
Benchmarking: MongoDB, system / network [MD5 32/64]... DONE
Benchmarking: Mozilla, Mozilla key3.db [SHA1 3DES 32/64]... DONE
Benchmarking: mssql, MS SQL [SHA1 128/128 SSE2 4x2]... DONE
Benchmarking: NT [MD4 128/128 SSE2 4x4]... DONE
Benchmarking: Office, 2007/2010/2013 [SHA1 128/128 SSE2 4x2 / SHA512 128/128 SSE2 2x AES]... DONE
Benchmarking: OpenBSD-SoftRAID [PBKDF2-SHA1 128/128 SSE2 4x2]... DONE
Benchmarking: Oracle12C [PBKDF2-SHA512 128/128 SSE2 2x]... DONE
Benchmarking: PDF [MD5 SHA2 RC4/AES 32/64]... DONE
Benchmarking: postgres, PostgreSQL C/R [MD5 32/64]... DONE
Benchmarking: radius, RADIUS authentication [MD5 32/64]... DONE
Benchmarking: 7z, 7-Zip (512K iterations) [SHA256 128/128 SSE2 4x AES]... DONE
Benchmarking: skey, S/Key [MD4/MD5/SHA1/RMD160 32/64]... DONE
Benchmarking: SSH [RSA/DSA/EC/OPENSSH (SSH private keys) 32/64]... DONE
Benchmarking: VNC [DES 32/64]... DONE
Benchmarking: ZIP, WinZip [PBKDF2-SHA1 128/128 SSE2 4x2]... DONE
  :
All 403 formats passed self-tests!

まるで品評会だな。そんな事に感心してないで、現役のOpenBSD(7.6)をクラックしろよ。

eq$ ./john BF
Using default input encoding: UTF-8
Loaded 1 password hash (bcrypt [Blowfish 32/64 X3])
Cost 1 (iteration count) is 512 for all loaded hashes
  :
Proceeding with wordlist:./password.lst, rules:Wordlist
123456           (joy)
1g 0:00:01:39 DONE 2/3 (2024-11-25 07:09) 0.01007g/s 52.41p/s 52.41c/s 52.41C/s 123456..password

そんじゃ、FreeBSD 14.1からのパスワードはどうよ?

eq$ ./john FB
Using default input encoding: UTF-8
Loaded 1 password hash (sha512crypt, crypt(3) $6$ [SHA512 128/128 SSE2 2x])
Cost 1 (iteration count) is 5000 for all loaded hashes
  :
Proceeding with wordlist:./password.lst, rules:Wordlist
123456           (joy)
1g 0:00:00:02 DONE 2/3 (2024-11-25 07:19) 0.4950g/s 439.1p/s 439.1c/s 439.1C/s 123456..green

弱いパスワードは、あっという間に突破されますよ。注意せましょう。

README

羽田と成田 なんて本を読んだ。

著者は、国土交通省 航空局長(だった人、今は天下りしてる)。相当なバイアスがかかっていると、思って 読めよ。オイラーが本を読む時、いの一番に確認するのは著者の経歴ね。 それから、前書き、目次を見て、どんな地図かを確認。それから、後書きを 先に読んじゃう。

のっけから、成田国際空港の歴史から始まる。他の候補地として、霞ヶ浦 とかが有ったなんて、初めて知ったよ。成田闘争、散発的には知ってた けど、こうして、まとめを開陳されると、すさまじい事だったのね。

その後遺症は、成田空港駅でよく体験した。パスポートと航空券を拝見。それから 厳重な荷物検査。

この重い章に続いて、韓国がらみで、羽田が国際空港に変貌して行く とっかかりが説明されてる。日韓ワールドカップ開催。韓国の金浦空港から 羽田へチャーター便を飛ばして、輸送力を増強したい。それを契機に 国際化のスタートを切る。成田をなだめる為、役人が姑息な理由付け。

羽田から一番遠くにある国内空港は、石垣島にある。距離にして、1947Km。 この同心円の事をペリメーターと言うらしい。金浦空港は、文句なくこの同心円内 に収まる。だったら、国内線と見做してもいいでしょ。それに、冷え込んでいる 両国の虹のかけ橋、友好の象徴にもなるよね。 この論理は、後々、羽田 - 虹橋(中国・上海)便にも適用 される。理屈なんて、後付けでどうにでもなるって、良い例だわな。

一日4便の枠が設定された。JAL、ANA、大韓航空、アシアナ航空ね。まあ、文句が 出ない選定だわな。

そう言えば、アシナナ航空には思い出があるな。この4社の中で、アシアナ航空の ねーちゃん(CA)が一番美形。乗るならアシアナが前担当からの申し送り事項に はいっていた。ある時、乗客が極端に少ない事があった。そのせいか、キャバレー の顔見せよろしく、次々と酒を持ってねーちゃんが登場。随分と酔わせてもらった。エア・ホステス とは、よく言ったものである。


This year's Index

Home