WordPressのブルートフォースアタック

らら
らら

はじめに

今回は、緊急で、ブログ書いてます・・って

別件で、お客さんからWordPress攻撃されたから、調べてほしいとのことで・・・

うちで設置・設定してないので・・・あれなんですが・・

お願いされたので・・・調べてみた。。

最近のプラグインとか・・知らないしねぇ

ログ解析・・してみた。

どうも、ブルートフォースアタックぽい、パスワードは12桁で記号、英数字大文字小文字の組み合わせだったぽい・・

うーむ。何十年もかかるんじゃぁ・・と・・

最近は、ビットコインとかの生成ハード(グラボ複数枚構成のやつ)使って、やっているよとか・・巷で聞くけど・・

ログでは、?author=1 実行されていた・・・まだこのコマンドあるの?!!うちでは、最近WordPress設置しないので、疎いですが・・・

実行してみると・・動いちゃうのね・・・・うううぅ・・・

こうなると・・ログインIDはわかっちゃうので・・・パスワードだけの解析になっちゃうのね。

xmlrpc.phpもアクセスがあり・・これ、禁止にしてないのね・・・最近は、不具合なくなったからとはいえ、これでもユーザーIDわかるってしらないのかね・・

その後、wp-login.phpとかあって・・ログインされてる感じ、でプラグインのインストールで、wp-file-manager入れられて・・・

定番のPHPファイラーがいくつか・・・入れられてる感じで・・・

shell.phpとか、ss.phpとかファイルが上がってて・・・ありゃぁ・・って感じ・・

12桁でもパスされちゃうのね。。最近・・・・

どうなんだろか・・・作業者のPCからパスもれたのか・・どうなんだろか。。。あきらかに・・海外のIPからログインされてるしね、

ログだけでは、形跡は、あるけど・・ブルートフォースアタックで得たのか、単なるパスワード漏洩なのかまでは・・・

各コマンドの意味を晒してみる・・

日本じゃ、あんまり、こういう脆弱性ぽいの書くと、良くないよねぇみたいな風潮もあるが・・

海外じゃ。。逆・・・

ブルートフォースアタックってwordpressのログイン画面に対して、行われると思っている人たちもいるかと・・

WordPressの場合、xmlrpc.php REST APIだったりと間口がおおい、

この画面からだけじゃない・・・

WordPressのブルートフォースアタック

対策方法

?author=1でユーザー名の特定する

下記でアクセスすると・・


https://サイトドメイン/?author=1

下記へリダイレクトされます。

これでログインIDが取得できてしまいます。


https://サイトドメイン/author/ワードプレスのログインID

対応方法

functions.phpに下記を追加

最近のは、よくわからないので、動作するかは確認してください。


function disable_author_archive_url() {
	if (is_author()) {
		wp_safe_redirect(home_url(), 301);
//		wp_redirect(home_url());
		exit;
	}
}
add_action('template_redirect','disable_author_archive_url');

Edit Author Slugプラグインもあるけど、プラグインの脆弱性も怖いので・・

xmlrpc.phpでユーザー名の特定する

下記にxmlをPOSTする


https://サイトドメイン/xmlrpc.php

POSTするxml


<?xml version="1.0" encording="iso-8859-1"?>
  <methodCall>
    <methodName>wp.getUsersBlogs</methodName>
    <params>
      <param>
        <value>wordpressのユーザー</value>
      </param>
      <param>
        <value>wordpressのパスワード</value>
      </param>
    </params>
  </methodCall>

Linuxコンソールで・・


curl -d '<?xml version="1.0" encording="iso-8859-1"?><methodCall><methodName>wp.getUsersBlogs</methodName><params><param><value>wordpressのユーザー</value></param><param><value>wordpressのパスワード</value></param></params></methodCall>' https://サイトドメイン/xmlrpc.php

ID,パスが違うと・・こんな親切なメッセージを送ってくれる、Wordpress


<?xml version="1.0"?>
<methodResponse>
  <fault>
    <value>
      <struct>
        <member>
          <name>faultCode</name>
          <value><int>403</int></value>
        </member>
        <member>
          <name>faultString</name>
          <value><string>誤ったログイン/パスワードの組み合わせ。</string></value>
        </member>
      </struct>
    </value>
  </fault>
</methodResponse>

ID,パスが一致しちゃうと・・・こんな感じが返ってくる・・


<?xml version="1.0"?>
<methodResponse>
  <params>
    <param>
      <value>
      <array><data>
  <value><struct>
  <member><name>isAdmin</name><value><boolean>1</boolean></value></member>
  <member><name>url</name><value><string>https://サイトドメイン/</string></value></member>
  <member><name>blogid</name><value><string>1</string></value></member>
  <member><name>blogName</name><value><string>サイト名</string></value></member>
  <member><name>xmlrpc</name><value><string>https://サイトドメイン/xmlrpc.php</string></value></member>
</struct></value>
</data></array>
      </value>
    </param>
  </params>
</methodResponse>

対応方法

.htaccessで下記を設定するか。ブログを使っていないのであれば、xmlrpc.php消してもよいが、バージョンアップで入ってきちゃうと思うので・・


<Files "xmlrpc.php">
order deny,allow
deny from all
</Files>

あとは、これ


add_filter( 'xmlrpc_enabled', '__return_false' );

下記で


remove_action( 'wp_head', 'rsd_link' );

上記でlink rel="EditURI"を削除します。


<link rel="EditURI" type="application/rsd+xml" title="RSD" href="https://ドメイン/xmlrpc.php?rsd" />

REST API でユーザー名の特定する方法

これは、トークン取得してやらないとだめなんじゃ、コマンド叩くだけで、ログインIDはやばい・・内部的なユーザーID(シーケンシャル)ならまだ・・

下記でアクセスすると・・。


https://サイトドメイン/wp-json/wp/v2/users
https://サイトドメイン/?rest_route=/wp/v2/users

下記のjsonがもれなく帰ってきます


[{"id":1,"name":"ワードプレスのログインID","url":"","description":"","link":"https:.....

対応方法

functions.phpに下記を追加


function disable_rest_endpoints( $endpoints ) {
	if(isset($endpoints['/wp/v2/users'])) {
		unset($endpoints['/wp/v2/users']);
	}
	if(isset($endpoints['/wp/v2/users/(?P[\d]+)'])) {
		unset($endpoints['/wp/v2/users/(?P[\d]+)']);
	}
	return $endpoints;
}
add_filter('rest_endpoints', 'disable_rest_endpoints',10,1);

wp-sitemap-users-1.xmlでユーザー名の特定する

これは、初めて、知りました・・Ver5.5かららしい・・


https://サイトドメイン/wp-sitemap-users-1.xml

対応方法

functions.phpに下記を追加


add_filter('wp_sitemaps_enabled', '__return_false');

その他WordPressと推定されないために

絵文字JavaScriptとStyleSheetの削除

消すの下記


		<script type="text/javascript">
			window._wpemojiSettings = {"baseUrl":"https:\/\/s.w.org\/images\/core\/emoji\/11\/72x72\/","ext":".png","svgUrl":"https:\/\/s.w.org\/images\/core\/emoji\/11\/svg\/","svgExt":".svg","source":{"concatemoji":"https:\/\/www.maruko.com\/wordpress\/wp-includes\/js\/wp-emoji-release.min.js?ver=4.9.26"}};
			!function(e,a,t){var n,r,o,i=a.createElement("canvas"),p=i.getContext&&i.getContext("2d");function s(e,t){var a=String.fromCharCode;p.clearRect(0,0,i.width,i.height),p.fillText(a.apply(this,e),0,0);e=i.toDataURL();return p.clearRect(0,0,i.width,i.height),p.fillText(a.apply(this,t),0,0),e===i.toDataURL()}function c(e){var t=a.createElement("script");t.src=e,t.defer=t.type="text/javascript",a.getElementsByTagName("head")[0].appendChild(t)}for(o=Array("flag","emoji"),t.supports={everything:!0,everythingExceptFlag:!0},r=0;r<o.length;r++)t.supports[o[r]]=function(e){if(!p||!p.fillText)return!1;switch(p.textBaseline="top",p.font="600 32px Arial",e){case"flag":return s([55356,56826,55356,56819],[55356,56826,8203,55356,56819])?!1:!s([55356,57332,56128,56423,56128,56418,56128,56421,56128,56430,56128,56423,56128,56447],[55356,57332,8203,56128,56423,8203,56128,56418,8203,56128,56421,8203,56128,56430,8203,56128,56423,8203,56128,56447]);case"emoji":return!s([55358,56760,9792,65039],[55358,56760,8203,9792,65039])}return!1}(o[r]),t.supports.everything=t.supports.everything&&t.supports[o[r]],"flag"!==o[r]&&(t.supports.everythingExceptFlag=t.supports.everythingExceptFlag&&t.supports[o[r]]);t.supports.everythingExceptFlag=t.supports.everythingExceptFlag&&!t.supports.flag,t.DOMReady=!1,t.readyCallback=function(){t.DOMReady=!0},t.supports.everything||(n=function(){t.readyCallback()},a.addEventListener?(a.addEventListener("DOMContentLoaded",n,!1),e.addEventListener("load",n,!1)):(e.attachEvent("onload",n),a.attachEvent("onreadystatechange",function(){"complete"===a.readyState&&t.readyCallback()})),(n=t.source||{}).concatemoji?c(n.concatemoji):n.wpemoji&&n.twemoji&&(c(n.twemoji),c(n.wpemoji)))}(window,document,window._wpemojiSettings);
		</script>
		<style type="text/css">
img.wp-smiley,
img.emoji {
	display: inline !important;
	border: none !important;
	box-shadow: none !important;
	height: 1em !important;
	width: 1em !important;
	margin: 0 .07em !important;
	vertical-align: -0.1em !important;
	background: none !important;
	padding: 0 !important;
}
</style>

対応方法

functions.phpに下記を追加


remove_action('wp_head','print_emoji_detection_script', 7);
remove_action('admin_print_scripts','print_emoji_detection_script');
remove_action('wp_print_styles','print_emoji_styles');
remove_action('admin_print_styles','print_emoji_styles');

meta generatorの削除


<meta name="generator" content="WordPress 6.8" />

対策


remove_action('wp_head', 'wp_generator');

WordPressとプラグイン?ver=の削除


function remove_wp_pg_ver_css_js($src) {
	if(strpos($src, 'ver=')) {
		$src = remove_query_arg('ver',$src);
	}
	return $src;
}
add_filter('style_loader_src', 'remove_wp_pg_ver_css_js',9999);
add_filter('script_loader_src', 'remove_wp_pg_ver_css_js',9999);

link wlwmanifest削除

下記


<link rel="wlwmanifest" type="application/wlwmanifest+xml" href="http://ドメイン/wp-includes/wlwmanifest.xml" />

対策


remove_action( 'wp_head', 'wlwmanifest_link' );

WordPress管理画面にベーシック認証

ないよりは、あったほうが・・・

wp-login.phpにベーシック認証をかける

.htpasswdの作り方は・・ぐぐってください。。

.htaccess ファイル内容


<Files wp-login.php>
AuthType Basic
AuthUserFile /htpasswdのサーバーパス/.htpasswd
AuthGroupFile /dev/null
AuthName "Please enter your ID and password"
require valid-user
</Files>

/wp-admin/内すべてにベーシック認証をかける

.htaccessは、/wp-admin/内に作成します。

.htaccess ファイル内容


AuthType Basic
AuthUserFile /htpasswdのサーバーパス/.htpasswd
AuthGroupFile /dev/null
AuthName "Please enter your ID and password"
Require valid-user
<FilesMatch "(admin-ajax.php)$">
Satisfy Any
Order allow,deny
Allow from all
Deny from none
</FilesMatch>

最近のパスワードの桁数の強度?

下記から引用してます。

https://www.hivesystems.com/password

文字数 数字のみ 小文字のみ 大文字、小文字 数字、大文字、小文字 数字、大文字、小文字、記号
4 Instantly Instantly 3 secs 6 secs 9 secs
5 Instantly 4 secs 2 mins 6 mins 10 mins
6 Instantly 2 mins 2 hours 6 hours 12 hours
7 4 secs 50 mins 4 days 2 weeks 1 month
8 37 secs 22 hours 8 months 3 years 7 years
9 6 mins 3 weeks 33 years 161 years 479 years
10 1 hour 2 years 1k years 9k years 33k years
11 10 hours 44 years 89k years 618k years 2m years
12 4 days 1k years 4m years 38m years 164m years
13 1 month 29k years 241m years 2bn years 11bn years
14 1 year 766k years 12bn years 147bn years 805bn years
15 12 years 19m years 652bn years 9tn years 56tn years
16 119 years 517m years 33tn years 566tn years 3qd years
17 1k years 13bn years 1qd years 35qd years 276qd years
18 11k years 350bn years 91qd years 2qn years 19qn years

上記テーブルの単位

京の先なんて・・しらないよぉ・・

Table Abbreviation Word e.g. (10^n zeros)
k thousand 1,000.00
m million 1,000,000.00
bn billion 1,000,000,000.00
tn trillion 1,000,000,000,000.00
qd quadrillion 1,000,000,000,000,000.00
qn quintillion 1,000,000,000,000,000,000.00
sx sextillion 1,000,000,000,000,000,000,000.00
spt septillion 1,000,000,000,000,000,000,000,000.00
oct octillion 1,000,000,000,000,000,000,000,000,000.00
non nonillion 1,000,000,000,000,000,000,000,000,000,000.00
dec decillion 1,000,000,000,000,000,000,000,000,000,000,000.00

さいごに

とりあえず、急ぎで書いているのでまちがいがあるかもです。

では・・

関連記事