ZIPファイルのパスワードの解析実践及び複合化時間の予測

こんにちは。著者のActiveTKです。

今回は、パスワード付きZIPファイルのパスワード解析を実際に実践して、どれくらいの時間がかかるのか予測していきたいと思います。

まさか、ZIPファイルを4桁の数字のみのパスワードで暗号化している方はいませんよね?

一般のPCでも3分あれば解析出来てしまいますよ?

C#で総当たり

まず、C#で4桁の数字のみのパスワードを総当たりして、解析速度(1秒間にテスト出来るパスワードの数)を計測します。

ところが、C#にはパスワード付きZIPを展開する機能がありません。

なので、ZIPの展開はSharpZipLibというライブラリに頼りたいと思います。。

バージョン1

まず最初に考え付くのは、ループ文を利用して、単一スレッドで総当たりをする方法です。

int i = 0; // 現在の数
while (true) // 無限ループ
{
    string h = string.Format("{0:0000}", i); // 0埋めをする
    bool d = 合ってるか確認する関数(h);
    if (d)
    {
        Console.WriteLine("パスワードは" + h + "です!");
        break;
    }
    i++; // 現在の数を1プラスする
    if (i == 最大値) // パスワードが見つからなかった時の処理
        break; // ループから抜ける
}

このような、かなり単純なコードです。

このコードでは、5929という数字を解析するのに12分30秒かかり、 解析速度は7.9/s程度です。

つまり、1秒間に約8個のパスワードを試せるという計算です。

数字からすると早そうですが、 果たしてパスワード解析にしては早いのですかね。。?

バージョン2

先ほどのバージョン1を改善したバージョンです。

まず、マルチスレッド化。

先ほどのコードでは、同時に1つしか解析を進行出来ませんでしたが、複数のスレッドを作成して同時に解析する事により、CPUを最大限に活用します。

// スレッドを定義する
var attack = new ThreadStart(() =>
{
    while (true)
    {
        // 先ほどのとほぼ変わらない総当たりコード
    }
});
for (int i = 0; i != Environment.ProcessorCount; i++)
// CPUのスレッド数だけ総当たり用のスレッドを作成する
{

    new Thread(attack).Start();
}

次に、CPUをフルで活用する為に「プロセスのCPU優先度」を最大に設定します。

Process.GetCurrentProcess().PriorityClass = ProcessPriorityClass.RealTime;

これでどうでしょうか。

僕が実験してみたPCは4スレッドだったので、C#の総当たりスレッドも4つ生成されました。

「5929」という4桁の数字を解析するのにかかった時間は1分52秒でした。なので、 解析速度は約53.3/sです。

つまり、1秒間に53個のパスワードを試せるという事です。

バージョン1に比べると、ブロッキング時間が軽減出来た事により、速度が数十倍になりました。

バージョン3

無謀なアプリを作っていると、だんだんこんな感情が湧いてきました。

「普通に考えたらスペックが良い方が総当たり速度が速いんじゃね?」と。

なので、バージョン3ではC#を使わずに、PHPでプログラムを書き、超高スペックなサーバー君に処理をお任せしてみます。

もちろん、サーバーに負荷がかかってしまうので所要時間は数分程度が限界ですが。

まずPHPでパスワード付きZIPを展開出来ないんじゃないか?!という疑問がありましたが、ZipArchiveクラスにsetPasswordという関数があり、C#よりも簡単に展開する事が出来ました。

function unzip(string $pw)
{
    $zip = new ZipArchive();
    $file_mod = 0755;
    $unzip_dir = "./Zip-Files/";
    if ($zip->open(filename) !== TRUE) 
      return FALSE;
    for ($i = 0; $i < $zip->numFiles; $i++) 
      if(file_exists($unzip_dir.$zip->getNameIndex($i)))
        @unlink($unzip_dir.$zip->getNameIndex($i));
    $zip->setPassword($pw);
    if ($zip->extractTo($unzip_dir) !== TRUE) {
      $zip->close();
      return FALSE;
    }
    $files = [];
    for ($i = 0; $i < $zip->numFiles; $i++) {
      $files[] = $zip->getNameIndex($i);
      if(file_exists($unzip_dir.$zip->getNameIndex($i)))
        chmod($unzip_dir.$zip->getNameIndex($i), $file_mod);
    }
    $zip->close();
    return $files;
  }

上の関数は、ネットで拾ってきたZIPを展開する関数をパスワード指定出来るように編集したものです。

unzip(パスワード);

と実行すれば、パスワードがあっていれば配列、間違っていればfalseが返ってきます。

これをループ文で実行するだけです。

今までと同じく、 解析速度を計測すると、、393.3/sとなりました。

つまり、1秒間に約400個のパスワードを試せるという事です。

まとめ

今までの解析速度から半数解析時間を推測すると、

バージョン1バージョン2バージョン3
3桁/数字
10^3/2=500
63秒9秒1秒
4桁/数字
10^4/2=5000
10分31秒1分33秒12秒
5桁/数字
10^5/2=50000
1時間
45分29秒
15分38秒2分7秒
6桁/数字
10^6/2=500000
17時間
34分51秒
2時間
36分20秒
21分11秒
4桁/半角英数字
(10^4+26^4)/2
=233488
8時間
12分35秒
1時間
13分00秒
9分53秒
4桁/全半混合英数字
(10^4+26*2^4)/2
=3660808
5日と
8時間
43分13秒
19時間
4分43秒
2時間
35分07秒
6桁/全半混合英数字
(10^6+26*2^6)/2
=9885804832
39年248日と
10時間
8分20秒
5年321日と
16時間
46分02秒
290日と
22時間
05分32秒
注: 小数点以下切り捨てで計算、うるう年は未換算

となります。

一週間で半数が解けてしまうかどうかを基準にして、利用可能か判断すると、最低でもパスワードは6桁以上の、半角・全角混合の英数字にするべきだという事です。

なお、そのようなパスワードですらも高性能なサーバーの力を借りれば1年で解析出来てしまうという事です。

なので、パスワードは

・半角・全角の英語と数字を含んだ6桁以上

・1年に1回は変える!

ようにしましょう。

コメントする

メールアドレスが公開されることはありません。 が付いている欄は必須項目です