Linux用マルウェアスキャナーを自作する:PythonとYARAで軽量・高速・柔軟なツールを構築

Python tutorial - IT technology blog
Python tutorial - IT technology blog

なぜマルウェアスキャニングツールを自作するのか?

kinsingxmrigといった不審なプロセスによって、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セキュリティへの理解を深める素晴らしい方法でもあります。まずはシンプルなルールセットから始めて、実際のニーズに合わせて徐々にアップグレードしていきましょう!

Share: