pythonでハッシュ値からパスワードを辞書攻撃で探す
以前の記事ではパスワードを平文で保存するのではなく、単方向ハッシュ関数を用いハッシュ値として記憶しておくものだと説明しました。果たしてそれだけで安全は確保できたと言えるのでしょうか。JavaScriptで簡易パスワード認証サイトを作成しましたが、そのページのソースコードを表示するとパスワードのハッシュ値がそのまま書かれてしまっています。いくらハッシュ値が数学的に不可逆であっても、答えが分かっている数学の式のように簡単に突破されてしまいます。
3*□=12□に0から順番に値を入れていき、解と同じになるのを探す方法が総当たり攻撃
list : 2, 4, 5, 10, 12, 14□にlistの値を順番に入れていき、解と同じになるのを探す方法が辞書攻撃
3*□=12
簡単に両方を説明する
■総当たり攻撃は解析にかかる時間は長くなるが、ほぼ正解を見つけることができる。
■辞書攻撃は解析にかかる時間は短くなるが、正解が分からない場合もある。
今回は辞書攻撃を使い、以前設定したハッシュ値から元の平文パスワードを探してみることにする。もちろん自分で設定したので、平文パスワードは辞書リストに追加されており、解析は必ず出来るだろう。私の辞書リストを公開することは勘弁願いたいので、適当に5個程度書いておき今回はそれを使う。赤文字は設定した平文パスワードを表している。
file name : passlist
qwerty
12345
pass
password
test
以前設定した平文パスワードとそのハッシュ値は次のもの。ハッシュアルゴリズムはmd5を使用していた。
password = 5f4dcc3b5aa765d61d8327deb882cf99
pythonで辞書攻撃用のコードを書く前に、ハッシュ値を求める方法を紹介しておく。
python2.4(2.5は下位互換の為、使用可能)
>>> import md5python2.6以降
>>> print md5.new("password").hexdigest()
5f4dcc3b5aa765d61d8327deb882cf99
>>> import hashlib
>>> print hashlib.md5("password").hexdigest()
5f4dcc3b5aa765d61d8327deb882cf99
以上を踏まえたうえで、pythonで辞書ファイルを読み込み一行ずつ比べていくコードを書いていきます。
まず読み込む辞書ファイルを作ります。本来なら何千・何万行もの辞書を用意しますが、今回は5〜10行でも問題ないです。
PythonCode ****$ cat words
test
password
aaaa
qwerty
asdfgh
zxcvbn
次に辞書ファイルと検索するハッシュ値をコマンドライン引数とする『hashcheck.py』を作ります。
hashcheck.py#!/usr/bin/env python
# -*- coding: utf-8 -*-
import sys
import string
import hashlibparam = sys.argv
f = open(param[1], 'r')
hash = param[2]for line in f:
#hashlib.md5(line)だと改行コードを含むのでstring.strip(line)とする
hashed = hashlib.md5(string.strip(line)).hexdigest()
if hash == hashed:
#UTF-8の文字を表示する場合、先頭に『u』を付ける
print u"パスワード:"+line,
print u"ハッシュ値:"+hashed
f.close()
#一致を見つけたらこれ以上続ける意味がないのでプログラムを終了させる
sys.exit()print "パスワードが辞書に存在しません"
f.close()
第一引数に辞書ファイル名、第二引数に検索するハッシュ値(password)で実行してみます。
PythonCode ****$ ./hashcheck.py words 5f4dcc3b5aa765d61d8327deb882cf99
パスワード:password
ハッシュ値:5f4dcc3b5aa765d61d8327deb882cf99
このように20行程度のコードでハッシュ値からパスワードが分かってしまいました。もちろん辞書に存在しないパスワードなら問題はありませんが、簡単なパスワードを使用している場合は注意が必要です。またハッシュ値を見つかりづらい所で保管する必要があり、ハッシュアルゴリズムを知られないようにする工夫も必要です。