4
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

サーバ攻撃で狙い撃ちされたphpMyAdminのユーザ名とパスワードをランキングしてみた

Last updated at Posted at 2020-02-24

概要

自前のサーバーでブログを開設した途端、世界中から怪しい攻撃が来た話を読んで、自サーバのログも調べてみたら同様のクラッキング目的と思われるアクセスが来ていたことが確認できました。

その中で、phpMyAdminのユーザ名とパスワードを当てずっぽうで当てにきている攻撃がたくさんあったので、それらについて攻撃された回数をカウントし、ランキングしてみました。

解析

さくらVPSで契約したサーバ(CentOS 6.10)のApache HTTPサーバのログ(/var/log/httpd/access_log)を取得しました。
中身を見てみると下記のようにphpMyAdminに当てずっぽうのユーザ名とパスワードでアクセスが試みられていることがわかります。

xxx.xxx.xxx.xxx - - [04/Oct/2018:02:57:44 +0900] "GET /phpMyAdmin/index.php?pma_username=root&pma_password=1qaz3edc5tgb&server=1 HTTP/1.1" 200 14538 "-" "Mozilla/5.0"
xxx.xxx.xxx.xxx - - [04/Oct/2018:02:57:46 +0900] "GET /phpMyAdmin/index.php?pma_username=root&pma_password=1qaz@123&server=1 HTTP/1.1" 200 14546 "-" "Mozilla/5.0"
xxx.xxx.xxx.xxx - - [04/Oct/2018:02:57:59 +0900] "GET /phpMyAdmin/index.php?pma_username=root&pma_password=1qaz@2wsx&server=1 HTTP/1.1" 200 14533 "-" "Mozilla/5.0"
xxx.xxx.xxx.xxx - - [04/Oct/2018:02:58:03 +0900] "GET /phpMyAdmin/index.php?pma_username=root&pma_password=1qaz@WSX&server=1 HTTP/1.1" 200 14543 "-" "Mozilla/5.0"
xxx.xxx.xxx.xxx - - [04/Oct/2018:02:58:06 +0900] "GET /phpMyAdmin/index.php?pma_username=root&pma_password=1qaz@WSX3edc&server=1 HTTP/1.1" 200 14538 "-" "Mozilla/5.0"
xxx.xxx.xxx.xxx - - [04/Oct/2018:02:58:09 +0900] "GET /phpMyAdmin/index.php?pma_username=root&pma_password=1qaz@wsx&server=1 HTTP/1.1" 200 14533 "-" "Mozilla/5.0"

pythonを用いてこれらの情報を抽出しヒストグラム情報を作りました。
pythonコードは末尾に付録として載せておきます。

解析対象ログの基本情報

取得期間:2018/09/26 ~ 2020/01/22
全体の行数:658858行
うちphpMyAdminのパスワードを当てずっぽうで当てにきている攻撃と思われるもの:186499行

ユーザ名の攻撃回数(全25種類)

順位 ユーザ名 回数
1 root 185504
2 wordpress 183
3 admin 138
4 wp 47
5 blog 45
6 pma 45
7 shop 43
8 money 42
9 popa3d 40
10 joomla 39
11 http 35
12 ueer 35
13 project 35
14 nginx 33
15 apache 33
16 sql 33
17 db 32
18 nas 32
19 shopdb 31
20 dbs 31
21 web 28
22 backupdb 6
23 wordspress 5
24 backup 2
25 backups 2

パスワードの攻撃回数(全953種類のうち上位30種類)

順位 パスワード 回数
1 pass 408
2 password 372
3 361
4 admin 359
5 123 347
6 root 344
7 123456 326
8 welcome 323
9 r00t 322
10 monkey 322
11 whatever 322
12 abc123 321
13 aa123456 321
14 123123 319
15 mysql 318
16 login 318
17 111111 318
18 password123 318
19 1234567890 317
20 access 317
21 666666 316
22 apache 315
23 oracle 315
24 654321 315
25 root123 315
26 123qwe 314
27 1234567 314
28 12345678 314
29 pass123 314
30 letmein 313

(ついでに)攻撃されたパスとその回数(全2種類)

順位 パス 回数
1 /phpmyadmin/index.php 173687
2 /phpMyAdmin/index.php 12812

考察

全ランキングはこちらにおいておきます。

ユーザ名はrootが圧倒的多数で、ほかはwordpress、blogなどのブログ関連用語、admin、pmaなどデータベース関連用語、apacheなどサーバ関連用語などが並んでいる感じでした。
sshサーバへの攻撃ログを解析した記事と比べるとsshへの攻撃では人名やOS名などが狙い撃ちされているのに対し、phpMyAdminへの攻撃ではブログ関係用語が狙われているなど、微妙に傾向の違いがあって面白いです。

パスワードのトップ3は「pass」「password」「」(なし)でした。
突出してどれかが多いということはなくありがちなものが全体的にまんべんなく狙われている感じでした。
こちらで言われているような「最悪なパスワード」の傾向とも一致しているように思えます。
パターンとしては下記のようなものがありました。

  • 有名名詞(PC関連) - root、mysql、access、apahe、oracleなど
  • 有名名詞(一般) - welcome、monkey、freedom、money、dragonなど
  • 数字羅列 - 123456、123123、111111、1234567890、666666など
  • 「password」またはその派生 - pass、password、Password、passw0rd、p@$$w0rd123など
  • キーボード順番押し - qwerty、qazwsx、qazxcv!@#、zaq1zaq1、qwertなど
  • 上記の組み合わせ - abc123、aa123456、password123、123qwe、admin123など

ついでに攻撃対象となったパスも集計しました。
攻撃対象となっていたのは以下の2種類だけでした。

  • /phpmyadmin/index.php
  • /phpMyAdmin/index.php

不正アクセス防止のための狙われるアクセス先リスト

狙われやすいURLについて
の記事で言及されていたパスと比べるとバリエーションが少ないのは、今回が"pma_password="が含まれているという条件で行を抽出したあとの結果であるためと考えられます。実際抽出条件を「"phpMyAdmin"が含まれていること」にしてログを眺めてみると、今回挙げた2種類以外のパスへのアクセスも試みられていることがわかりました。

おわりに

僕はセキュリティの知識はほぼないので「どうしたら安全か」はわかりませんが、少なくともデフォルトパスにおいたデータベースにありがちなユーザ名とパスワードをセットで使うのはやめようと思いました。

本文は以上です。
ここまでお読みいただきありがとうございました。
以下は付録です。

付録

ログから必要情報を抽出するためのpythonコードです。
今回は下記のような行を解析対象としました。

xxx.xxx.xxx.xxx - - [04/Oct/2018:02:58:03 +0900] "GET /phpMyAdmin/index.php?pma_username=root&pma_password=1qaz@WSX&server=1 HTTP/1.1" 200 14543 "-" "Mozilla/5.0"

"pma_password="が含まれていれば解析対象とみなします。
ここからスペースや特定の文字、キーワードで分割するなどして、情報を抽出します。
情報とその取り出し方の例を示します。
ここはログファイルの書き出しの設定などによって異なる可能性があるので適宜修正してください。

情報 取り出し方
ipアドレス スペースで分割して0番目
タイムスタンプ "["と"]"で囲まれた文字列
Method (GET, POSTなど) スペースで分割して6番目。ただし初めの文字がダブルクオテーションなのでそれは削除する
phpMyAdminのパス スペースで分割されたうち7番目の要素から"?"の直前まで
ユーザ名 "pma_username="と"&"の間の文字列
パスワード "pma_password="と"&"の間の文字列。(ただしこの部分のqueryでurlが終了している場合があり、そのときは後ろの"&"が存在しないため、スペースの直前までとする)

全部の行がこのやり方で抽出できているとは限らないのですが、多少例外があっても影響は少ないだろうと思って無視しています。そのあたりは適当です。
要素を取り出したあと、注目する情報(今回はphpMyAdminのパス、ユーザ名、パスワード)の回数を数えます。
最後に降順にソートして標準出力に表示します。

ソースコード全体は下記のような感じです。

from collections import defaultdict

with open('/path/to/access_log','r') as f:
    logs = f.readlines()

# phpMyAdminへの攻撃の抽出
pma_attacks = [log for log in logs if 'pma_password' in log]

# ip, time_stamp, method, path, username, passwordを抽出
# ログの形式に合わせて適宜書き換える
extracted_pma_infos = []
for pma in pma_attacks:
    #ipアドレスはスペース区切りの0番目の要素
    ip = pma.split(' ')[0] 
    #タイムスタンプは"["から"]"までの文字列
    time_stamp = pma.split('[')[1].split(']')[0] 
    #Method(POST、GETなど)はスペース区切りの6番目の要素から1文字目を削除したもの
    method = pma.split(' ')[5][1:]
    #pathはスペース区切りの6番目の要素のうち"?"の直前までの文字列
    path = pma.split(' ')[6].split('?')[0]
    #ユーザ名は"pma_username="から直近の"?"までの文字列
    username = pma.split('pma_username=')[1].split('&')[0]
    #パスワードは"pma_password="から直近の"?"までの文字列
    password = pma.split('pma_password=')[1].split('&')[0]
    #パスワードが最後のクエリの場合の例外処理
    #このとき、直近の"?"が存在せずログの末尾まで含まれてしまうので、スペースの直前できる
    if ' ' in password:
        password = password.split(' ')[0]
    extracted_pma_infos.append([ip,time_stamp,method,path,username,password])

# 攻撃されたパス、ユーザ名、パスワードのヒストグラムを作る
pathlist = defaultdict(int)
unlist = defaultdict(int)
pslist = defaultdict(int)
for pma in extracted_pma_infos:
    path = pma[3]
    un = pma[4]
    ps = pma[5]
    pathlist[path]+=1
    unlist[un]+=1
    pslist[ps]+=1

# 引数のヒストグラムデータ(dict_obj)を解析して降順のリストを作る関数
def orderedlistFromDict(dict_obj):
    count = list(set([dict_obj[v] for v in dict_obj]))
    count.sort()
    orderedlist = []
    for c in count[::-1]:
        for key in dict_obj:
            if dict_obj[key] == c:
                orderedlist.append((key,dict_obj[key]))
    return orderedlist

# 攻撃されたパス、ユーザ名、パスワードの回数順のリスト
ordered_path = orderedlistFromDict(drlist)
ordered_un = orderedlistFromDict(unlist)
ordered_ps = orderedlistFromDict(pslist)

# パスランキングの表示
for val in ordered_path:
    print(val[0],val[1],sep=',')

# ユーザ名ランキングの表示
for val in ordered_un:
    print(val[0],val[1],sep=',')

# パスワードランキングの表示
for val in ordered_ps:
    print(val[0],val[1],sep=',')
4
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?