マルウェアとは何か〜WordPressに設置されていたマルウェアを解析してみた結果、恐ろしいソフトがでてきた〜
結論
WordPressのドキュメントルート上に、
wp-includes/wp-pages.lib.php
https://www.japan-secure.com/entry/how-to-perform-wordpress-security-measures.html
がもし設置されていたら、それはマルウェアなので
直ぐに削除すること。
※ 抜本的な対策ではないので、以下の対策も実行しましょう。
マルウェアとは
概要
マルウェア(Malware)とは、不正かつ有害な動作を行う意図で作成された悪意のあるソフトウェアや悪質なコードの総称です。
感染者
利用者のPC
webサーバ
今回はwebサーバが被害にあった。
どんな攻撃をされるか
情報漏えい
サイト改ざん
不正な機能実行
他サイトへの攻撃
基本的に、OSコマンド・インジェクション攻撃と同じです。
※ 徳丸本より
今回解析して出てきたソフトの概要
※ 安全のため、ローカル上での実行&不完全な実行をしております
マルウェアを解析してみた結果、中身はこんなソフトだった。
画面を見てみると、このような機能があることがわかった。
基本データの収集
ファイル操作
このファイルにアクセスしさえすれば、すきなphpファイルをuploadできるようなので、
実質サーバに対してはなんでもできる。
恐ろしい。
いきさつ
友人のwordpressサイトが攻撃され、サイトが全く見れなくなった。
マルウェアと思わしき怪しいファイルがあったので解析してみた。
解析
ファイルのパーミッションを確認
wp-includes/wp-pages.lib.php
このファイルだけ755。怪しさ満点。
中身を見てみる
怪しさ満点の難読文字列。
base64_decodeとeval。
まず、base64コードで普通は書かない。
この時点で「内容を知られたくないから難読化」していることがわかる。
そして、evalはとても危険。
evalに渡された文字列はphpとして実行されてしまうからだ。
どんな文字列も渡すことができるので、
実質phpでできることならなんだって出来てしまう。
evalをechoに変えて出力
またbase64_decodeがでてきた。どうやら何重にもbase64_encodeをしているようだ。
この作業をひたすら繰り返していく。5〜6回繰り返した気がする。
php関数もencodeされていた
どんどん解析してくと、普段使っているphp関数がbase64_encodeされていた。
ものすごい執念を感じる。
http://manual.xwd.jp/function.gzinflate.html
こちらの関数はdeflate圧縮されたものを解答する関数。
なんらかのソフトウェアっぽい雰囲気が出てきた。
phpコードがでてきた
更に解析をすすめると、phpコードがでてきた。
変数名にランダムなアルファベットを使用していたので、わかりやすくかえた。
冒頭のランダム文字列を1文字ずつ読み込んで、ASCII文字コードに変換している。
PHP関数 - ASCIIコードを特定の文字に変換 - chr() - PHP入門 - Webkaru
さらにそれを文字に変換している。
さらにphpコードがでてきた
また大量の文字列がでてきた。
これをまたechoすると、また大量の文字列。
ここでgzinflateがでてきたので、おそらく後ろのbase64_decodeが本体ファイルと思われる。
ここまで難読化されていると、マルウェアであることは確実と思われる。
ただ、結局このままではどのようなマルウェアがわからないので、
安全な状況を確保してから解凍することにした。
安全な状況を(出来る限り)確保してから解凍する
最悪な状況にそなえ、
普段使わない(そして壊れてもいい)windows + dockerにて、php実行環境を構築し、
スタンドアローンで実行。
※ 良い子は真似しちゃダメ
冒頭のソフトが現れた。
まとめ
マルウェアはすごく難読化されている。
phpで出来ているものの場合、evalやbase64_decode + 意味不明な文字列
が含まれていると怪しい。
マルウェア対策については、別の記事に書こうと思います。
追記
FilesMan? 運営サイトがハッキングされバックドアがサーバーに設置される! ( Twitter ) - 無題な濃いログ - Yahoo!ブログ
有名なバックドアだということが判明。
オーキド「こういうものには つかいどきがあるのじゃ」〜Rubyよりもシェルのほうが良いときもある。プログラミング技術は要件に応じて使い分けよう〜
上司からの司令
上司「data.txtファイル中の、この数字を昇順にしてくれ。なるはやで」
1 45 31 2
上司が欲しい出力結果はこんな感じ。
1 2 31 45
Rubyを使用した場合
elements = [] File.open('data.txt') do |file| file.each_line do |row| elements << row.chomp.to_i end end p elements.sort_by {|a| a}
7行で出来た。
シェルを使用した場合
$ sort data.txt
1行で出来た。
オーキドの言葉「こういうものには つかいどきがあるのじゃ」
この言葉を思い出した。
おそらく僕がRubyを選択したら、この言葉を言われたと思う。
求められている要件
txtファイルの内容をソートしたい なるはやで
要件を満たす手段
1番目の要件を満たす言語は、いくらでもある。
ただ、「なるはや」という2番めの条件を入れると、
エクセルという選択肢も出てくる。
さらに知っているのであればシェルが一番早い。
求められている要件 別パターン
txtファイルをアップロードして、中身をソートしたい その内容をみんなで共有したい 社内でのみ使うのでセキュリティはあまくて良い なるはやで
要件を満たす手段
この場合は2番目の要件が重要で、共有サーバで見れる必要が出てくる。
ここで
「ファイルアップロード! データ共有!
webアプリだ! webアプリならRailsだ!」
わかる。でも他にも方法はある。
3番目の要件は
「セキュリティがそれなりに担保されているフルスタックフレームワークである必要は無い」
と捉えることもできる。
1機能のみをフルスタックフレームワークで書くのは大げさすぎる。
すると、「生phpで書く」という選択肢が出てくる。
ファイル分割したとしても2ファイルくらいで実装できてしまいそう。
(もちろん機能的に大きくなることが予想されるのであれば、
フレームワークという選択肢もあります)
技術は要件に応じて使い分けるのが大事
プログラミングはあくまで手段。
要件をなるべくすべて満たすような手段を選ぶことが、ビジネス的には大事そう。
とはいえ手段すべてをマスターすることはできない
マスターはしなくて良いので、それぞれの技術の特徴を抑えておくことがまずは大事。
抑えておくと、要件が降ってきたときにいろんな手段を思いつくことができる。
生PHP...小規模開発 Rails/PHPフレームワーク...中規模〜 Java…大規模な業務系開発(実績があるから) SQL...集計 統計解析…Python, R 組み込み…C コマンドライン上での簡単な処理...bash
※個人の見解です&ものすごくざっくりです
まとめ
弘法は筆を選ばないが、エンジニアは手段を選ぶほうがよい
そんなエンジニアになりたいと思った。
最近の出来事を元に脚本を書いてみる。その4
はじめに
この話はいい感じにフィクションです。
登場人物
わたし(26)…会社員
マスター(41)…バーのマスター
省吾(42)…常連
小杉(35)…常連
杏奈(30)…常連
あやか(26)…常連
M 今日はマスターの誕生日。僕はケーキをもって店に向かった。
マスター「おーいらっしゃい」 わたし「マスターこれ。あとでみんなで食べましょう」 マスター「お〜、ありがとうございます!」 小杉「さ〜っすが!君仕事できるね〜! ちゃんとろうそく41本?」 わたし「いや5本です」 小杉「だめだね〜!やっぱ仕事できないね〜!」
M 熱い手のひら返しをくらう。面白い人だ。
SE ドアの開く音
省吾「こんばんは」 杏奈「こんばんは〜」 マスター「いらっしゃい」 マスター「かろてんくんがケーキをもってきてくれたんですよ」 杏奈「……」 省吾「……おー。すごいね。泡いれますか。かろてんくん飲む?」 かろてん「いただきます!」
M シャンパンなんて早々飲む機会がないので嬉しい。
M ものすごく美味しいシャンパンをのんでいると、
なにやら省吾さんと杏奈さんが困惑した顔でアイコンタクトをしている。
杏奈「かろてんくんさぁ」(小声) わたし「はい?」 杏奈「ダメだよ、誕生日プレゼントは一人でかっちゃあ」(小声) わたし「なんでですか?」
M 私の質問に答えず、杏奈さんは省吾さんの方を見た。
杏奈「省吾さん、いつ出します?」 省吾「今のタイミングしかないんじゃないかな」 杏奈「ですよね〜」
M そういって杏奈さんは紙袋の中から箱を取り出した。
嫌な予感がする。
杏奈「マスター、誕生日おめでと〜!」
M ケーキがでてきた。
これでテーブルの上には大きなホールケーキが二つ。
ショートケーキと、チョコレートケーキ。
わたし「いや〜やっちゃいましたね」 省吾「いやいやかろてんくんわかってる。チョコレートケーキをあえて選ぶ所が良いよね」 わたし「ショートケーキかシュークリームで悩んだんですよ。危ないところでした」
SE ドアの開く音
あやか「マスターおくれた!誕生日おめでと!!」
M 常連がまたやってきた。
あやか「マスター見てこれ!シュークリーム!!」
M 省吾さんと僕は思わず、顔を見合わせた。
そのテーブルに、idは必要ですか?〜idの必要性とデータの一意性についてもう一度考えてみる
テーブルに必ずと行っていいほど入っているidカラム
普段扱っているテーブルには、だいたいidカラムが含まれている。
Ruby on Railsのmigration機能を使うと勝手にidカラムが入ったりする。
当たり前のように存在するidカラム。
そのidカラムの必要性について、もう一度考え直してみる。
忙しい人のために結論
idカラムが重要なのではない。重要なのはデータが一意に特定できること データが一意に特定できればidは必要ないことがある ただ、あったほうが後々便利なことも多い
こんな例について考えてみる
例えば30人の生徒がいるクラスに、同姓同名の生徒(山田太郎)がいたとする。
そして、このクラスの出席簿は名前カラムのみが存在している。
こんな感じ。
名前 |
---|
... |
山田太郎 |
山田太郎 |
... |
このとき、
-- 朝会にて。出席をとる先生。山田太郎は1人欠席してる。 先生「山田太郎さん」 山田太郎「はい!」 先生「山田太郎さんは元気だね。山田太郎さんは休みかな?」
となり、
どっちの山田太郎が出席していてどっちの山田太郎が休んでいるのかわかりゃしない。
30人から1人が特定できないのだ。困った。
解決策
生徒1人を特定できるように、カラムを増やしてやればよい。
出席番号 | 名前 |
---|---|
... | ... |
23 | 山田太郎 |
24 | 山田太郎 |
... | ... |
これで
-- 朝会にて。出席をとる先生 先生「23番!」 山田太郎(23)「はい!」 先生「24番!」 山田太郎(24)「はい!」
となり生徒の特定が可能だ。
番号で呼んでるせいで刑務所みたいになってるけど。
出欠というより点呼とってる。
一意性
このように、大量のデータの中から1つのデータを特定できる性質のことを、
「一意性」という。
データはなによりも「一意性」を持っていることが非常に重要である。
もし一意性がないと、
出欠が取れなかったり、
どっちの口座に振り込めばよいかわからなくなったり、
どの飛行機に乗ればよいかわからなくなったり、、、
一意性がないと困ることはいっぱいある。
兎にも角にもデータは一意性が大事。
ここでid
idはデータに「一意性」をもたせるためによく使われる。
idさえ降っておけば、idのおかげでレコードが1つに特定できるからだ。実にお手軽。
(そもそもidは「identification(識別)」の略)
でも、いつもidは必要かというと、そういうわけではない。
家族テーブル
下の名前 |
---|
太郎 |
次郎 |
三郎 |
「下の名前」だけで一意性が保たれる。
グローバルipアドレステーブル
ipアドレス |
---|
201.168.33.10 |
203.168.33.10 |
... |
そもそもグローバルipアドレスが一意。
都道府県・市テーブル
都道府県名 | 市名 | |
---|---|---|
東京都 | 府中市 | |
広島県 | 府中市 | |
北海道 | 伊達市 | |
福島県 | 伊達市 | |
... | ... |
# | 物理名 | 論理名 |
---|---|---|
1 | id | id |
2 | 名前 | name |
3 | レベル | level |
4 | アイテム1の名前 | item1_name |
5 | アイテム2の名前 | item2_name |
6 | アイテム3の名前 | item3_name |
コントローラ
Heroを1人取得しておきます。
class HeroController def index @hero = Hero.find(1) end end
view
<%= @Hero.name %> <%= @Hero.age %> <%= @Hero.level %> <%= @Hero.item1_name %> <%= @Hero.item2_name %> <%= @Hero.item3_name %>
仕様変更
このような状態で、
「Heroのアイテムを1つ増やしたい」という仕様変更があると、
* DBのカラムにitem4_nameを増やす * viewファイルにitem4_nameを出力するように修正
という変更が必要となってきます。
今回の場合、1コントローラ1viewなので修正範囲は狭いですが、
このテーブルが多くのコントローラ・viewで参照されている場合修正箇所はどんどん増えます。
またこの修正は、Heroのアイテムを増やすたびに行われます。
-- なんだかおかしなことになってきましたね。
B. 正規化する場合
そこでDBを第一正規化してみます。
第1正規化では、同一の情報のグループが繰り返し出現している部分を分離します。
DB(Hero, Itemテーブル)
Heroテーブル
# | 物理名 | 論理名 |
---|---|---|
1 | id | id |
2 | 名前 | name |
3 | レベル | level |
Itemテーブル
# | 物理名 | 論理名 |
---|---|---|
1 | id | id |
2 | HeroId | hero_id |
3 | アイテム名 | name |
controller
class HeroController def index @hero = Hero.find(1) @items = Item.where(hero_id: 1) end end
view
<%= @Hero.name %> <%= @Hero.age %> <%= @Hero.level %> <%= @items.each do |item| %> <%= item.name %> <% end %>
特徴
正規化されていないテーブルから、
「繰り返しが発生しているデータ(Item)」
「発生していないデータ(Hero)」
をわけたことにより、
繰り返しが発生している部分を、eachで回して処理することができるようになりました。
よって、アイテムが1つ増えてもDBやソースコードを修正する必要はありません。
おまけ
以下のようなときは正規化する必要はありません。
個数が変わらないことが保証されているデータ
例えば、人間のパーツをDBで管理することを例として考えてみましょうか。
人間は肺という臓器を2個持っています。腎臓も然り。
この場合、よほどのことがない限り3個以上にはならないので、
別テーブルに分ける必要はありません。
…変な例で失礼しました。