なぜマルウェアスキャニングツールを自作するのか?
kinsingやxmrigといった不審なプロセスによって、LinuxサーバーのCPU使用率が突如100%に達し、サーバーが「悲鳴を上げている」のを目にしたことがあるなら、手動でマルウェアを探し出す際のもどかしさがわかるはずです。現在の仮想通貨マイニングスクリプトやウェブシェルは、/tmpや/var/www/htmlといったディレクトリ内に非常に巧みに偽装されています。
多くの人はClamAVを選択しますが、このツールは起動するだけで800MBから1GB以上のRAMを消費することが多く、低スペックのVPSにとっては非常に贅沢なリソース消費となります。ここでYARAの出番です。重いシグネチャ(signature)ベースのスキャンではなく、YARAでは柔軟なルール(rules)を定義して、特定の特徴パターンからマルウェアを識別できます。これは、探偵に数百万人の犯罪者リストを暗記させるのではなく、詳細な特徴を記した説明書を渡すようなものです。
Pythonとyara-pythonを組み合わせることで、完全な自動化システムを構築できます。わずか数十行のコードで、定期的なスキャン、ファイルサイズによるフィルタリング、そして即時アラート送信が可能になります。
開発環境の構築
まず、サーバーにPython 3がインストールされている必要があります。UbuntuやDebian系のOSであれば、インストールプロセスは非常に迅速で、2分もかかりません。
1. ライブラリのインストール
# パッケージリストの更新
sudo apt update
# Python用のビルドツールとヘッダーのインストール
sudo apt install -y build-essential python3-dev
# Python用YARAライブラリのインストール
pip install yara-python
2. スキャンルールの定義 (YARA Rules)
スキャナーの強力さはルールのセットに依存します。ゼロから書く代わりに、GitHub上のコミュニティが公開している膨大なルールセットを活用することもできます。まずは、危険なPHP関数を捕捉するためのシンプルなファイル webshell_rules.yar を作成してみましょう。
rule Detect_Webshell_PHP {
strings:
$a = "eval(base64_decode"
$b = "shell_exec"
$c = "system($_GET"
condition:
any of them
}
Pythonによるマルウェアスキャンスクリプトの作成
ディレクトリを巡回する知的なスクリプトが必要ですが、アクセス権限の不足や巨大なゴミファイルのスキャンによってサーバーをダウンさせないように注意する必要があります。
メインロジックの処理
実際には、ハードドライブ全体をスキャンする必要はありません。通常、速度を最適化するためにスキャン対象を10MB以下のファイルに制限します。優れたスキャンスクリプトは、機密性の高いシステムファイルに接触した際の PermissionError をスムーズに処理する必要があります。
import yara
import os
import logging
# 後で確認できるようにログを記録
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(levelname)s - %(message)s',
filename='scanner.log'
)
def compile_rules(rule_path):
try:
return yara.compile(filepath=rule_path)
except yara.Error as e:
print(f"ルールのコンパイルエラー: {e}")
return None
def scan_directory(path, rules):
print(f"[*] スキャン中: {path}")
for root, _, files in os.walk(path):
for file in files:
file_path = os.path.join(root, file)
try:
# リソース節約のため10MB未満のファイルのみスキャン
if os.path.getsize(file_path) > 10 * 1024 * 1024:
continue
matches = rules.match(file_path)
if matches:
msg = f"[!] 疑わしいファイルを検出: {file_path} (ルール: {matches})"
print(msg)
logging.warning(msg)
except (PermissionError, OSError):
continue
if __name__ == "__main__":
rules = compile_rules('webshell_rules.yar')
if rules:
# 重要なディレクトリに焦点を当てる
scan_directory('/var/www/html', rules)
print("--- スキャン完了 ---")
Regex(正規表現)による最適化のヒント
最近のマルウェアは、コードの難読化(obfuscation)技術をよく使用します。そのため、複雑な亜種を捕らえるためにYARAルール内でRegexを使用することをお勧めします。作成したRegexを過信しないでください。私はよく toolcraft.app の Regex テスター を使ってパターンを素早くテストしています。このツールはブラウザ上で動作し、システムにロードする前に、パターンが実際のシェルコードのサンプルと正確に一致するかを確認するのに役立ちます。
自動化と監視
手動でスクリプトを実行するのは時間がかかります。深夜にサーバーが自動で実行するように設定しましょう。
1. Cronによる定期スキャンの設定
午前2時はトラフィックが最も低くなる時間帯であり、マルウェアスキャンに理想的です。crontab -e に以下の行を追加します。
0 2 * * * /usr/bin/python3 /root/scripts/scanner.py
2. Telegramによる即時アラート通知
ログを読み続ける代わりに、異常を検知した際にスクリプトが自動でスマートフォンにメッセージを送信するようにします。requests ライブラリを使用して数行のコードを追加するだけです。
import requests
def alert_telegram(message):
token = "YOUR_BOT_TOKEN"
chat_id = "YOUR_CHAT_ID"
payload = {"chat_id": chat_id, "text": message}
requests.post(f"https://api.telegram.org/bot{token}/sendMessage", data=payload)
3. パフォーマンスに関する注意点
サーバーで重要なサービスが稼働している場合は、スクリプト実行時に nice または ionice コマンドを使用してください。例:nice -n 19 python3 scanner.py。これにより、OSがWebサーバーやデータベースのリソースを優先するようになり、スキャナーがユーザーサービスを中断させるのを防ぐことができます。
マルウェアスキャナーを自作することは、コストの節約になるだけでなく、Linuxセキュリティへの理解を深める素晴らしい方法でもあります。まずはシンプルなルールセットから始めて、実際のニーズに合わせて徐々にアップグレードしていきましょう!

