Featured image of post 辞書攻撃もしたい - 攻撃手法の切り替え

辞書攻撃もしたい - 攻撃手法の切り替え

英単語リストを使った辞書攻撃機能を追加。Iteratorインターフェースを守ることで、ブルートフォースと辞書攻撃を自由に切り替えるポリモーフィズムを学びます。

前回は、BruteForceIterator を作成し、パスワード生成ロジックをオブジェクトに閉じ込めました。

今回は、もっと現実的な攻撃手法である「辞書攻撃(Dictionary Attack)」を実装します。辞書攻撃とは、よく使われるパスワード(“password”, “123456”, “admin” など)のリストを使って攻撃する方法です。

辞書攻撃用のIteratorを作る

辞書攻撃も、使う側から見れば「次々とパスワード候補が出てくる」という点ではブルートフォースと同じです。 つまり、next メソッドを持つ同じインターフェースのクラスを作れば、メインのコードを変えずに攻撃手法だけを切り替えられるはずです。

DictionaryIterator.pm を作成します。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
package DictionaryIterator;
use Moo;
use experimental qw(signatures);

# 辞書ファイルのパス
has dict_file => (
    is       => 'ro',
    required => 1,
);

# ファイルハンドル(内部で保持)
has _fh => (
    is  => 'rw',
);

# 初期化時(BUILD)にファイルを開く
sub BUILD ($self, $args) {
    open my $fh, '<', $self->dict_file or die "辞書ファイルが開けません: $!";
    $self->_fh($fh);
}

# 次の単語を返す
sub next ($self) {
    my $fh = $self->_fh;

    # ファイルから1行読む
    my $line = <$fh>;

    # ファイルの末尾ならundefを返す
    return undef unless defined $line;

    chomp $line; # 改行削除
    return $line;
}

# デストラクタ(オブジェクト消滅時にファイルを閉じる)
sub DEMOLISH ($self, $in_global_destruction) {
    close $self->_fh if $self->_fh;
}

1;

このクラスは、数字をインクリメントするのではなく、ファイルを1行ずつ読み込んで返します。しかし、外から見れば next を呼ぶだけです。

攻撃ツールのアップグレード

では、メインのスクリプト cracker_poly.pl を書き換えて、攻撃手法を選べるようにしましょう。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
use v5.36;
use PasswordLock;
use BruteForceIterator;
use DictionaryIterator;
use Getopt::Long;

# オプション処理
my $mode = 'brute'; # デフォルトはブルートフォース
GetOptions("mode=s" => \$mode);

my $iterator;

# モードによってIteratorを切り替える(ポリモーフィズム!)
if ($mode eq 'brute') {
    say "モード: ブルートフォース攻撃";
    $iterator = BruteForceIterator->new(length => 3);
}
elsif ($mode eq 'dict') {
    say "モード: 辞書攻撃";
    # 簡易辞書ファイルを作成して使う
    open my $fh, '>', 'passwords.txt';
    print $fh "password\nadmin\n777\n123456\n";
    close $fh;

    $iterator = DictionaryIterator->new(dict_file => 'passwords.txt');
}
else {
    die "不明なモードです: $mode";
}

my $lock = PasswordLock->new;

say "攻撃を開始します...";

# ここは共通! $iteratorが何者か気にする必要はない
while (defined(my $attempt = $iterator->next)) {
    if ($lock->unlock($attempt)) {
        say "解除成功! パスワードは [ $attempt ] です!";
        exit;
    }
}

say "失敗しました...";

ポリモーフィズム(多態性)の威力

このコードの重要な点は、while ループの部分が一切変更されていないことです。

1
while (defined(my $attempt = $iterator->next)) {

$iteratorBruteForceIterator のインスタンスだろうと、DictionaryIterator のインスタンスだろうと、メインのロジックは「next を呼んで試す」ことだけを知っていれば良いのです。

これをポリモーフィズム(多態性)と呼びます。

共通のインターフェース(ここでは next メソッド)に従う限り、中身の実装を自由に交換できる。これこそがオブジェクト指向設計の強力な武器であり、拡張性の高いツールを作る秘訣です。

クラス図で見ると、この関係性がよく分かります。Mainスクリプトは具体的なクラス(BruteForceIteratorなど)に直接依存するのではなく、「nextメソッドを持つ何か(Iterator)」を使っているだけなのです。

	classDiagram
    class MainScript {
        +run()
    }

    class Iterator {
        <<interface>>
        +next() String
    }

    class BruteForceIterator {
        +next() String
        -length
        -current
    }

    class DictionaryIterator {
        +next() String
        -fh
    }

    MainScript --> Iterator : 使う
    BruteForceIterator ..|> Iterator : 実装
    DictionaryIterator ..|> Iterator : 実装

    note for Iterator "PerlではMoo::Roleまたは\nダックタイピングで実現"

次回予告

ここまでで、私たちは知らず知らずのうちに、ある有名なデザインパターンを実装していました。

次回は最終回。これまで作ってきたものが、ソフトウェア工学の歴史の中で何と呼ばれているのか、その正体を明かします。そして、このパターンがPerlの標準機能や他の言語でどのように使われているかを総括します。

" >前回記事: 組み合わせ生成機を作る - Mooでループをオブジェクトにする

comments powered by Disqus