概要
普段仕事で使っているパスワードがかなり辞書攻撃に弱そうなんだけど、そのパスワードってどれくらい効力があるんだろ?と疑問に思ったので、辞書攻撃するスクリプトを作ってみた。
辞書攻撃用コード
analyse_term()はuser_idやメールアドレスを解析して、辞書を作成する関数。例えば、cof@qiita.comからは、'cof@qiita', 'qiita', 'com', 'qiita.com', 'cof'
というような辞書を作成する。
read_dictionaly()は所定の辞書を読見込む関数。ネットで調べたところざっと50万語くらいの辞書はあるようであったが、今回は後述する7語の辞書を使用した。
create_direct_word()は、作成した辞書に含まれるワードそのものを順次生成する関数。
create_indirect_word()は、辞書に含まれるワードに指定された文字数を足した文字列を生成する関数(例えば、辞書にboyが含まれ、2文字が指定されていた場合に、boy01とか、boyaaとかを生成する関数)
なお、パスワードが正しいかどうかを==で判定しているが、これは例えばzip等のファイル回答の解析結果に置き換えても構わない。
import itertools
def analyse_term(term):
ans = []
if not term:
return ans
for symbol in '!"@#$%&\'()=~-^\\|`{}[]:*;+<>,./?\_':
if symbol in term:
for t in term.split(symbol):
ans.append(t)
ans += analyse_term(t)
return ans
def read_dictionaly(dictionaly_file):
f = open(dictionaly_file)
words = set(map(lambda x: x.replace('\n','').replace('\r',''), f.readlines()))
f.close()
return words
def create_direct_word(dictionaly):
for term in dictionaly:
yield term
def create_indirect_word(dictionaly,string,additional_num):
for term in dictionaly:
for s in itertools.combinations_with_replacement(string,additional_num):
s = ''.join(s)
yield s+term
yield term+s
def dictionaly_attack(user_id, string, dictionaly_file, max_additional_num, answer):
dictionaly = set(analyse_term(user_id))
print dictionaly
dictionaly |= set(read_dictionaly(dictionaly_file))
print dictionaly
for term in create_direct_word(dictionaly):
#print term
if term == answer:
return term
for i in range(1,max_additional_num+1):
for term in create_indirect_word(dictionaly,string,i):
#print term
if term == answer:
return term
return False
辞書ファイル
とりあえず思いついたまま作成してみた。
boy
girl
guy
man
woman
gentleman
lady
メインスクリプト
使用文字を小文字、スペース、数字に絞り、user_idをcof@qiita.com、パスワードをgirl012としてアタックしてみたところ、およそ0.03秒で発見できた。
# coding: utf-8
# Here your code !
from attack import dictionaly_attack
import time
user_id = 'cof@qiita.com'
dictionaly_file = "dictionaly.txt"
password = 'gir012'
string = 'abcdefghijklmnopqrstuvwxyz 0123456789'
start = time.time()
ans = dictionaly_attack(user_id, string, dictionaly_file, 3, password)
elapsed_time = time.time() - start
print ans,password
print ("elapsed_time:{0}".format(elapsed_time)) + "[sec]"
実行結果
辞書が小さいからなのかもしれないが、意外にあっさりとパスワードを発見することができた。普段仕事で使っているパスワードも、いいとこ1分もつかどうかくらいじゃなかろうか。
set(['cof@qiita.com', 'cof@qiita', 'qiita', 'cof', 'com', 'qiita.com'])
set(['boy', 'woman', 'cof@qiita.com', 'cof@qiita', 'lady', 'qiita', 'gentleman', 'cof', 'girl', 'com', 'guy', 'qiita.com', 'man'])
girl012 girl012
elapsed_time:0.0296669006348[sec]