Cài đặt Linux Malware Detect (LMD) kết hợp ClamAV trên CentOS Stream 9: Bảo vệ Web Server khỏi Mã Độc

CentOS tutorial - IT technology blog
CentOS tutorial - IT technology blog

Khi web server bị upload shell mà không hay

Hồi năm ngoái, một client nhờ mình check server CentOS đang chạy WordPress. Traffic bình thường, website vẫn lên, không có gì bất thường — cho đến khi mình thấy mấy file PHP lạ nằm trong /var/www/html/wp-content/uploads/. File ảnh nhưng extension .php.jpg, thực chất là webshell. Kẻ tấn công đã dùng nó gần 3 tuần để mine crypto trên server mà không ai biết.

Từ đó mình mới nghiêm túc setup malware scanner, thay vì chỉ tin vào firewall và SELinux. Bài này mình chia sẻ cách dùng LMD (Linux Malware Detect) kết hợp ClamAV trên CentOS Stream 9 — bộ đôi này phù hợp cho VPS và shared hosting chạy web server.

Thêm chút bối cảnh: hồi CentOS 8 EOL, mình phải migrate gấp 5 server sang Rocky Linux trong 1 tuần. Trải nghiệm đó dạy mình một điều — tool nào ít phụ thuộc package distro-specific thì port được nhanh hơn nhiều. LMD cài từ source, ClamAV có trong EPEL, cả hai chạy ổn từ CentOS 7 đến Stream 9 mà hầu như không cần chỉnh gì.

Tại sao cần cả hai thay vì chỉ một?

LMD và ClamAV cover các góc khác nhau — ghép lại mới đủ:

  • LMD — chuyên detect malware web: webshell PHP, backdoor, script injection. Signature database của LMD được xây từ honeypot thực tế, nên rất “sát” với các loại mã độc nhắm vào web server Linux.
  • ClamAV — engine antivirus nhanh và phổ thông. Khi LMD dùng ClamAV làm backend, tốc độ scan tăng 3-4x so với built-in engine của LMD.

Dùng ClamAV một mình: signature không đủ bao phủ webshell PHP mới. Dùng LMD một mình: scan chậm, tốn resource. Kết hợp lại mới có được setup thực tế cho production.

Cài đặt LMD và ClamAV trên CentOS Stream 9

Bước 1: Cài ClamAV qua EPEL

CentOS Stream 9 cần EPEL repo để lấy ClamAV. Nếu chưa có EPEL:

dnf install -y epel-release
dnf install -y clamav clamd clamav-update

Update signature database ngay sau khi cài:

freshclam

Nếu gặp lỗi lock file, xóa đi rồi chạy lại:

rm -f /var/lock/subsys/clamd.scan
freshclam

Bước 2: Cài Linux Malware Detect từ source

LMD không có trong repo chính thức — phải tải thẳng từ rfxn.com. Script cài của họ khá gọn:

cd /tmp
curl -LO https://www.rfxn.com/downloads/maldetect-current.tar.gz
tar xzf maldetect-current.tar.gz
cd maldetect-*/
bash install.sh

Script cài LMD vào /usr/local/maldetect/ và tạo symlink tại /usr/local/sbin/maldet. Kiểm tra version:

maldet -v

Cấu hình chi tiết

Chỉnh conf.maldet

File cấu hình chính là /usr/local/maldetect/conf.maldet. Những option quan trọng cần thay đổi:

vi /usr/local/maldetect/conf.maldet
# Bật email alert khi phát hiện mã độc
email_alert="1"
email_addr="[email protected]"

# Tự động cách ly file bị phát hiện vào quarantine
quarantine_hits="1"

# Tự động clean script inject (xóa đoạn inject, giữ lại file gốc)
quarantine_clean="1"

# Dùng ClamAV engine — quan trọng nhất, bật lên để tăng tốc
scan_clamscan="1"
clamscan_path="/usr/bin/clamscan"

# Giới hạn CPU/IO để không ảnh hưởng production
scan_cpunice="19"
scan_ionice="6"

Cái scan_clamscan="1" là thứ mình hay quên set khi dựng server mới. Không có nó, LMD dùng engine riêng — chậm hơn khoảng 3-4 lần so với ClamAV backend.

Thêm web root vào scan tự động

LMD tạo sẵn cron daily tại /etc/cron.daily/maldet, mặc định scan /tmp/dev/shm. Tạo cron riêng để kiểm soát giờ chạy — mình thường set lúc 2 giờ sáng, khung giờ ít traffic nhất:

crontab -e
# Scan web root lúc 2:00 AM mỗi đêm
0 2 * * * /usr/local/sbin/maldet --scan-all /var/www/html >> /var/log/maldet-cron.log 2>&1

# Update signature LMD mỗi ngày lúc 1:00 AM
0 1 * * * /usr/local/sbin/maldet -u >> /var/log/maldet-update.log 2>&1

ClamAV cũng cần update signature định kỳ:

echo "0 */12 * * * root /usr/bin/freshclam --quiet" > /etc/cron.d/freshclam

Whitelist thư mục không cần scan

Một số WordPress plugin dùng base64_decode, eval, hoặc encode string — trông giống shell nên LMD hay báo false positive. Thêm path vào file ignore của LMD (mỗi dòng một path):

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

Scan lại path đó để verify LMD đã bỏ qua đúng:

maldet -a /var/www/html/wp-content/plugins/wordfence

Kiểm tra và Monitoring

Test với EICAR file

EICAR là file test chuẩn quốc tế — không phải mã độc thật nhưng mọi antivirus đều nhận diện. Dùng để verify setup hoạt động đúng trước khi để automation chạy một mình:

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

Nếu setup đúng, output sẽ báo hit và file bị move vào quarantine:

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

Scan thủ công khi nghi ngờ

CPU đột ngột lên 90% lúc 3 giờ sáng, traffic đến từ IP chưa thấy bao giờ, hoặc đơn giản là có gì đó “không ổn” — đây là lúc cần scan ngay thay vì chờ cron:

# Scan toàn bộ web root
maldet -a /var/www/html

# Scan chỉ file thay đổi trong 3 ngày gần nhất
maldet -r /var/www/html 3

# Xem report sau scan
maldet --report

Quản lý quarantine

File bị cách ly không bị xóa — chỉ move sang thư mục riêng. Website không crash ngay, bạn có thời gian xem xét trước khi quyết định xóa thật. Xem danh sách:

ls -la /usr/local/maldetect/quarantine/

Khôi phục file bị cách ly nhầm (false positive):

maldet --restore /usr/local/maldetect/quarantine/filename

Theo dõi log realtime

# Event log — mọi hoạt động phát hiện/cách ly
tail -f /usr/local/maldetect/logs/event_log

# Scan log — chi tiết từng lần scan
tail -f /usr/local/maldetect/logs/scan_log

Vài điều rút ra từ thực tế

Sau khoảng nửa năm chạy setup này trên production, mình đúc kết được mấy điểm:

  • Quarantine không bằng phòng ngừa: LMD phát hiện sau khi file đã lên server rồi. Kết hợp thêm ModSecurity để chặn upload malicious content từ đầu mới đủ lớp bảo vệ.
  • Bật inotify monitor nếu cần realtime: LMD hỗ trợ inotifywait để theo dõi thay đổi file tức thì — set inotify_monitor="1" trong config. Tốn resource hơn, nhưng phát hiện được shell ngay khi upload xong.
  • Log rotation cho maldet-cron.log: Scan hàng đêm, log file tích lại nhanh hơn bạn nghĩ — đặc biệt khi quét thư mục có nhiều file nhỏ. Thêm entry vào /etc/logrotate.d/ để tự xoay log.
  • Test email alert ngay sau setup: Đừng chờ đến khi có sự cố mới phát hiện email không gửi được. Chạy test scan với EICAR và verify email về trước khi để automation.

Setup này mất khoảng 20 phút — bao gồm cả thời gian freshclam update signature lần đầu. Với server nhận file upload từ người dùng thì đây không phải tùy chọn, đây là bắt buộc. Mình học bài này theo cách khó nhất rồi.

Share: