シェルがアップロードされていても気づかなかったとき
去年のことだ。あるクライアントからWordPressを動かしているCentOSサーバーの調査を依頼された。トラフィックは正常、サイトは問題なく表示されていた。何も異常はなかった——/var/www/html/wp-content/uploads/に見慣れないPHPファイルを見つけるまでは。画像ファイルのふりをして.php.jpgという拡張子を持つそのファイルは、実際にはwebshellだった。攻撃者はそれを使って約3週間、誰にも気づかれずにサーバーで暗号通貨のマイニングを行っていた。
それ以来、ファイアウォールとSELinuxだけを信頼するのをやめ、マルウェアスキャナーのセットアップを真剣に取り組むようになった。この記事では、CentOS Stream 9でLMD(Linux Malware Detect)とClamAVを組み合わせて使う方法を紹介する。このコンビはWebサーバーを動かすVPSや共有ホスティングに最適だ。
少し背景を説明すると、CentOS 8がEOLになったとき、1週間で5台のサーバーをRocky Linuxへ緊急移行しなければならなかった。その経験から学んだことがある——ディストリビューション固有のパッケージへの依存が少ないツールほど、移行がずっとスムーズだということだ。LMDはソースからインストールし、ClamAVはEPELから入手できる。どちらもCentOS 7からStream 9まで、ほぼ設定変更なしで問題なく動作する。
なぜ片方だけでなく両方が必要なのか
LMDとClamAVはそれぞれ異なる側面をカバーしている——組み合わせて初めて十分な防御になる:
- LMD — Webマルウェアの検出に特化:PHP webshell、バックドア、スクリプトインジェクション。LMDのシグネチャデータベースは実際のハニーポットから構築されているため、LinuxのWebサーバーを狙ったマルウェアの検出精度が非常に高い。
- ClamAV — 高速で汎用性の高いアンチウイルスエンジン。LMDがClamAVをバックエンドとして使用すると、LMD組み込みエンジンと比べてスキャン速度が3〜4倍向上する。
ClamAVだけを使う場合:シグネチャが新しいPHP webshellを十分にカバーできない。LMDだけを使う場合:スキャンが遅く、リソースを消費する。両方を組み合わせて初めて、本番環境で実用的なセットアップが実現できる。
CentOS Stream 9へのLMDとClamAVのインストール
ステップ1:EPELからClamAVをインストール
CentOS Stream 9でClamAVを入手するにはEPELリポジトリが必要だ。EPELがまだない場合:
dnf install -y epel-release
dnf install -y clamav clamd clamav-update
インストール後すぐにシグネチャデータベースを更新する:
freshclam
ロックファイルエラーが発生した場合は削除してから再実行する:
rm -f /var/lock/subsys/clamd.scan
freshclam
ステップ2:ソースからLinux Malware Detectをインストール
LMDは公式リポジトリには含まれていない——rfxn.comから直接ダウンロードする必要がある。インストールスクリプトはシンプルだ:
cd /tmp
curl -LO https://www.rfxn.com/downloads/maldetect-current.tar.gz
tar xzf maldetect-current.tar.gz
cd maldetect-*/
bash install.sh
スクリプトはLMDを/usr/local/maldetect/にインストールし、/usr/local/sbin/maldetにシンボリックリンクを作成する。バージョンを確認する:
maldet -v
詳細な設定
conf.maldetの編集
メイン設定ファイルは/usr/local/maldetect/conf.maldetだ。変更すべき重要なオプションは以下の通り:
vi /usr/local/maldetect/conf.maldet
# マルウェア検出時のメールアラートを有効化
email_alert="1"
email_addr="[email protected]"
# 検出されたファイルを自動的に隔離
quarantine_hits="1"
# インジェクトスクリプトを自動クリーン(インジェクト部分を削除、元ファイルを保持)
quarantine_clean="1"
# ClamAVエンジンを使用 — 最重要設定、スキャン高速化のために有効化
scan_clamscan="1"
clamscan_path="/usr/bin/clamscan"
# 本番環境への影響を抑えるためCPU/IOを制限
scan_cpunice="19"
scan_ionice="6"
scan_clamscan="1"は、新しいサーバーを構築するときによく設定し忘れる項目だ。これがないとLMDは独自エンジンを使用し、ClamAVバックエンドと比べて約3〜4倍遅くなる。
Webルートを自動スキャンに追加
LMDは/etc/cron.daily/maldetに日次cronを自動作成し、デフォルトで/tmpと/dev/shmをスキャンする。実行時間を制御するために独自のcronを作成する——トラフィックが最も少ない午前2時に設定するのが通例だ:
crontab -e
# 毎晩午前2:00にWebルートをスキャン
0 2 * * * /usr/local/sbin/maldet --scan-all /var/www/html >> /var/log/maldet-cron.log 2>&1
# 毎日午前1:00にLMDシグネチャを更新
0 1 * * * /usr/local/sbin/maldet -u >> /var/log/maldet-update.log 2>&1
ClamAVもシグネチャを定期的に更新する必要がある:
echo "0 */12 * * * root /usr/bin/freshclam --quiet" > /etc/cron.d/freshclam
スキャン不要なディレクトリのホワイトリスト登録
WordPressのプラグインの中にはbase64_decode、eval、文字列エンコードを使用するものがある——シェルのように見えるためLMDが誤検知を報告することがある。LMDのignoreファイルにパスを追加する(1行に1パス):
echo "/var/www/html/wp-content/plugins/wordfence" >> /usr/local/maldetect/ignore_paths
echo "/var/www/html/wp-content/plugins/your-plugin" >> /usr/local/maldetect/ignore_paths
そのパスを再スキャンしてLMDが正しく除外していることを確認する:
maldet -a /var/www/html/wp-content/plugins/wordfence
テストとモニタリング
EICARファイルでテスト
EICARは国際標準のテストファイルだ——実際のマルウェアではないが、すべてのアンチウイルスが検出する。自動化を単独で実行する前に、セットアップが正しく動作することを確認するために使用する:
echo 'X5O!P%@AP[4\PZX54(P^)7CC)7}$EICAR-STANDARD-ANTIVIRUS-TEST-FILE!$H+H*' > /tmp/eicar-test.com
maldet -a /tmp/eicar-test.com
セットアップが正しければ、出力にhitが表示され、ファイルが隔離に移動される:
maldet(12345): {hit} {eicar} /tmp/eicar-test.com [MD5: 44d88612...]
maldet(12345): {scan} 1 hits found
maldet(12345): {quarantine} /tmp/eicar-test.com moved to quarantine
不審なときの手動スキャン
CPUが突然90%に跳ね上がる、見慣れないIPからのトラフィックがある、あるいは単純に「何かがおかしい」——こういうときはcronを待たずにすぐスキャンが必要だ:
# Webルート全体をスキャン
maldet -a /var/www/html
# 直近3日間に変更されたファイルのみスキャン
maldet -r /var/www/html 3
# スキャン後のレポートを表示
maldet --report
隔離の管理
隔離されたファイルは削除されない——専用のディレクトリに移動されるだけだ。サイトはすぐにクラッシュしないため、実際に削除する前に確認する時間がある。一覧を表示する:
ls -la /usr/local/maldetect/quarantine/
誤って隔離されたファイルを復元する(誤検知):
maldet --restore /usr/local/maldetect/quarantine/filename
リアルタイムでログを監視
# イベントログ — 検出・隔離のすべてのアクティビティ
tail -f /usr/local/maldetect/logs/event_log
# スキャンログ — 各スキャンの詳細
tail -f /usr/local/maldetect/logs/scan_log
実際の運用から得た教訓
このセットアップを本番環境で約半年間運用して得た知見をまとめる:
- 隔離より予防が重要:LMDはファイルがサーバーに既にアップロードされてから検出する。最初から悪意のあるコンテンツのアップロードをブロックするModSecurityも組み合わせて初めて、十分な多層防御になる。
- リアルタイムが必要な場合はinotifyモニターを有効化:LMDは
inotifywaitによるリアルタイムのファイル変更監視をサポートしている——configでinotify_monitor="1"を設定する。リソースは多く消費するが、シェルがアップロードされた直後に検出できる。 - maldet-cron.logのログローテーション:毎晩スキャンすると、特に小さなファイルが多いディレクトリをスキャンする場合、ログファイルが思ったより早く溜まる。
/etc/logrotate.d/にエントリを追加して自動ローテーションを設定する。 - セットアップ直後にメールアラートをテストする:問題が起きてからメールが送れないことに気づくのでは遅い。EICARでテストスキャンを実行し、自動化に任せる前にメールが届くことを確認する。
このセットアップにかかる時間は約20分——freshclamの初回シグネチャ更新時間も含めて。ユーザーからのファイルアップロードを受け付けるサーバーにとって、これはオプションではなく必須だ。私はこれを最も辛い方法で学んだ。
