Linux 恶意软件扫描器 — 多阶段威胁检测(MD5、SHA-256、HEX、YARA、统计分析),ClamAV 集成,实时 inotify 监控,隔离/清除/恢复操作,以及多渠道告警(电子邮件、Slack、Telegram、Discord)。
(C) 2002-2026, R-fx Networks <[email protected]>
(C) 2026, Ryan MacDonald <[email protected]>
依据 GNU GPL v2 许可
原生扫描引擎提速 43 倍 — 原生扫描流水线已使用批量并行处理完全重写。基于约 10,000 个文件的真实基准测试:
| 版本 | 运行时间 | 文件 | 命中 |
|---|---|---|---|
| v1.6.6 | 1,217s | 9,931 | 35 |
| v2.0.1 | 28s | 9,931 | 35 |
MD5 和 HEX 签名匹配现在使用批量 grep 与 Aho-Corasick 并行工作线程,消除了每次扫描中逐文件模式编译开销和约 500,000 次子进程 fork。可通过 scan_workers 配置并行工作线程数量(默认:auto)。
其他亮点:扫描生命周期管理(--kill、--pause、--stop/--continue、-L)、带有 CPU 硬件自动检测的 SHA-256 哈希扫描(scan_hashtype)、原生 YARA 扫描(scan_yara=1)、Discord webhook 告警、Slack/Telegram 告警修复、原生引擎中的 ClamAV hex 通配符支持,以及整个代码库中的 200 多项错误修复。完整详情请参阅 CHANGELOG。
./install.sh
maldet -a /home/?/public_html
maldet -r /home/?/public_html 2
maldet -co scan_yara=1 -a /home/?/public_html
maldet -q SCANID
maldet -m users
maldet -u
---
## 1. 简介
LMD 的架构、检测阶段与受支持的平台。
Linux Malware Detect(LMD)是一款依据 GNU GPLv2 许可证发布的 Linux 恶意软件扫描器,设计上针对共享托管环境中面临的威胁。它利用网络边缘入侵检测系统的威胁数据,提取攻击中实际使用的恶意软件并生成检测签名。此外,威胁数据还来自用户通过 LMD 的 checkout 功能提交的样本以及恶意软件社区资源。
LMD 专注于传统防病毒产品经常漏报的恶意软件类别:PHP 木马(Shell)、JavaScript 注入器、base64 编码的后门、IRC 机器人,以及其他针对共享托管用户账户而非操作系统内部的 Web 应用层威胁。
**检测阶段**
- MD5 文件哈希匹配,用于精确定位威胁
- 通过带有并行工作进程的原生批量 grep 引擎进行 HEX 模式匹配
- 复合签名(csig)扫描:支持多模式布尔逻辑(AND/OR/阈值)、不区分大小写匹配、宽字符(UTF-16LE)匹配以及有界间隔通配符
- 原生 YARA 规则扫描,支持完整模块和自定义规则
- 统计字符串长度分析,用于检测混淆威胁(base64、gzinflate)
- ClamAV 集成,配合 LMD 维护的 ClamAV 签名提供扩展覆盖
**扫描与监控**
- 扫描所有文件、最近修改的文件或列表中的文件
- 通过内核 inotify 实现实时文件监控(创建/修改/移动事件)
- 通过 ModSecurity2 `inspectFile` 钩子对 HTTP 上传进行扫描
- 后台扫描,适用于无人值守的大规模操作
- 每次扫描可使用包含/排除正则表达式过滤
**隔离与响应**
- 隔离队列,以零权限方式存储文件
- 按扫描 ID 批量隔离/恢复
- 针对特定签名的清理规则,用于移除恶意软件
- 完整文件恢复(内容、属主、权限、mtime)
**告警与报告**
- HTML + 文本电子邮件告警,采用统一的 rfxn 设计(青色品牌、卡片条目)
- SMTP 中继支持(TLS/SSL),适用于没有本地 MTA 的环境
- Slack Block Kit、Telegram MarkdownV2、Discord embed 的逐条目告警
- 可通过 `alert/custom.d/` 覆盖自定义告警模板
- 扫描报告包含每个文件的命中详情,并按命中类型彩色标识
**基础设施**
- 自动链接 ClamAV 签名,实现双引擎覆盖
- 每日 cron 作业,自动检测 12 种以上托管控制面板
- CPU/IO 资源控制(nice、ionice、cpulimit)
- 签名与版本自动更新
- 支持 systemd 服务单元和 SysV init 脚本
### 受支持的平台
LMD 可在任何装有 bash 和标准 GNU 工具的 Linux 发行版上运行。已测试的平台:
| 平台 | 初始化系统 | 包配置路径 |
|----------|-------------|---------------------|
| RHEL / Rocky / AlmaLinux 8, 9, 10 | systemd | `/etc/sysconfig/maldet` |
| CentOS 6, 7 | SysV / systemd | `/etc/sysconfig/maldet` |
| Debian 10, 11, 12 | systemd | `/etc/default/maldet` |
| Ubuntu 20.04, 22.04, 24.04 | systemd | `/etc/default/maldet` |
| Gentoo | OpenRC | — |
| Slackware | SysV | — |
| FreeBSD | — | — (部分支持;不支持 inotify) |
---
## 2. 安装
在系统上安装、升级和移除 LMD。
附带的 `install.sh` 脚本处理所有安装任务。之前的安装会被自动备份。```bash
./install.sh
安装程序:
/usr/local/maldetect/usr/local/sbin/ 中创建 maldet 符号链接/etc/cron.daily/maldetconf.maldet)、自定义签名和忽略文件先前的安装会保存到 /usr/local/maldetect.bk{PID},并通过 maldetect.last 符号链接指向最近的备份。
默认路径:
/usr/local/maldetect/usr/local/sbin/maldet/etc/cron.daily/maldet/usr/lib/systemd/system/maldet.service所有面向用户的设置及其默认值。完整参考请参阅 man maldet(1)。
主配置文件为 /usr/local/maldetect/conf.maldet。所有选项均附有注释,便于配置。除非另有说明,选项使用 0/1 表示禁用/启用。
也可以使用 -co 标志在运行时覆盖配置:```bash
maldet -co quarantine_hits=1,email_addr=[email protected] -a /home
### 3.1 通用选项
| 变量 | 用途 | 默认值 |
|----------|---------|---------|
| `autoupdate_signatures` | 通过 cron 每日自动更新签名 | `1` |
| `autoupdate_version` | 通过 cron 每日自动更新 LMD 版本 | `1` |
| `autoupdate_version_hashed` | 对照上游校验 LMD 可执行文件的 SHA-256 哈希(若不可用则回退到 MD5) | `1` |
| `sigup_interval` | 通过独立 cron 任务(`/etc/cron.d/maldet-sigup`)进行自动签名更新检查的间隔小时数;0 = 禁用 | `6` |
| `cron_prune_days` | 隔离区/会话/临时数据的保留天数 | `21` |
| `cron_daily_scan` | 通过 cron 启用每日自动扫描 | `1` |
| `scan_days` | 每日 cron 扫描中回溯查找已修改文件的天数 | `1` |
| `import_config_url` | 下载远程配置覆盖项的 URL | — |
| `import_config_expire` | 导入配置的缓存过期时间(秒) | `43200` |
| `sig_import_md5_url` | 下载自定义 MD5 签名的 URL | — |
| `sig_import_hex_url` | 下载自定义 HEX 签名的 URL | — |
| `sig_import_yara_url` | 下载自定义 YARA 规则的 URL | — |
| `sig_import_sha256_url` | 下载自定义 SHA-256 签名的 URL | — |
| `sig_import_csig_url` | 下载自定义复合签名的 URL | — |
| `session_legacy_compat` | 在生成 TSV 会话文件的同时生成旧版纯文本会话文件:`auto`(检测旧格式会话)、`1`(始终生成)、`0`(仅 TSV) | `auto` |
### 3.2 告警
| 变量 | 用途 | 默认值 |
|----------|---------|---------|
| `email_alert` | 扫描后启用电子邮件告警 | `0` |
| `email_addr` | 告警收件人地址 | `[email protected]` |
| `email_subj` | 邮件主题行模板 | `maldet alert from $(hostname)` |
| `email_ignore_clean` | 当所有命中均被清理时抑制告警 | `1` |
| `email_panel_user_alerts` | 命中检测时向面板用户发送告警 | `0` |
| `email_panel_from` | 面板用户告警的 From 头 | `[email protected]` |
| `email_panel_replyto` | 面板用户告警的 Reply-To 头 | `[email protected]` |
| `email_panel_alert_subj` | 面板用户告警的主题行 | `maldet alert from $(hostname)` |
| `email_format` | 邮件格式:`text`、`html` 或 `both` | `html` |
| `smtp_relay` | SMTP 中继 URL(例如 `smtps://smtp.gmail.com:465`) | — |
| `smtp_from` | SMTP 中继投递的发件人地址 | — |
| `smtp_user` | SMTP 认证用户名 | — |
| `smtp_pass` | SMTP 认证密码 | — |
| `slack_alert` | 启用 Slack 文件上传告警 | `0` |
| `slack_subj` | Slack 上传的文件名 | `maldet alert from $(hostname)` |
| `slack_token` | Slack Bot API 令牌(作用域:`files:write`、`files:read`) | — |
| `slack_channels` | 以逗号分隔的频道名称或 ID 列表 | `maldetreports` |
| `telegram_alert` | 启用 Telegram 告警 | `0` |
| `telegram_bot_token` | Telegram Bot API 令牌 | — |
| `telegram_channel_id` | Telegram 聊天或群组 ID | — |
| `discord_alert` | 启用 Discord Webhook 告警 | `0` |
| `discord_webhook_url` | 用于发送告警的 Discord Webhook URL | — |
### 3.3 扫描选项
| 变量 | 用途 | 默认值 |
|----------|---------|---------|
| `scan_hashtype` | 阶段 1 的哈希算法:`auto`、`sha256`、`md5`、`both` | `auto` |
| `scan_max_depth` | find 的最大目录深度 | `15` |
| `scan_min_filesize` | 可扫描文件的最小大小 | `24` 字节 |
| `scan_max_filesize` | 可扫描文件的最大大小 | `2048k` |
| `scan_hexdepth` | HEX 签名匹配的字节深度 | `262144` |
| `scan_hex_chunk_size` | HEX+CSIG 扫描中每个微批处理包含的文件数(范围:1024-20480) | `10240` |
| `scan_csig` | 启用复合签名(csig)扫描(仅限原生模式) | `1` |
| `scan_workers` | MD5、SHA-256、HEX 和 CSIG 扫描阶段使用的并行工作进程数 | `auto` |
| `scan_cpunice` | 扫描进程的 nice 优先级(-19 到 19) | `19` |
| `scan_ionice` | IO 调度类别优先级(0-7) | `6` |
| `scan_cpulimit` | CPU 硬性限制百分比(0=禁用) | `0` |
| `scan_ignore_root` | 扫描时跳过 root 拥有的文件(默认不适用于监控模式;参见 `monitor_scan_owner_filters`) | `1` |
| `scan_ignore_user` | 跳过特定用户拥有的文件 | — |
| `scan_ignore_group` | 跳过特定组拥有的文件 | — |
| `scan_user_access` | 允许非 root 用户运行扫描 | `0` |
| `scan_user_access_minuid` | 用于创建 --mkpubpaths 用户目录的最小 UID | `100` |
| `scan_find_timeout` | find 文件列表生成的超时时间(0=禁用,最小 60 秒,建议 14400=4 小时) | `0` |
| `scan_export_filelist` | 将 find 结果保存到 tmp/find_results.last | `0` |
| `scan_tmpdir_paths` | -a/-r 扫描中包含的全局可写临时路径 | `/tmp /var/tmp /dev/shm /var/fcgi_ipc` |
| `string_length_scan` | 启用统计字符串长度分析 | `0` |
| `string_length` | 可疑字符串的最小长度 | `150000` |
| `scan_progress_log_interval` | 后台扫描期间进度日志检查点之间的秒数(0=禁用) | `60` |
| `scan_meta_cleanup_age` | 保留已完成/已终止扫描元数据文件的小时数(0=禁用) | `48` |
| `maint_compress_age` | 完成的会话文件在多少天后被 gzip 压缩(0=禁用) | `30` |
| `maint_archive_age` | 压缩后的会话在多少天后被归入每月存档(0=禁用) | `90` |
### 3.4 YARA 扫描
原生 YARA 扫描独立于 ClamAV 调用 `yara` 二进制文件(或 YARA-X 中的 `yr`),支持完整的 YARA 模块、编译规则以及 ClamAV 受限 YARA 子集无法处理的自定义规则文件。当两者都可用时,优先使用 `yr`(YARA-X)。当设置为 `auto` 时,仅在 ClamAV 不可用且找到 yara/yr 二进制文件时才启用原生 YARA,从而避免重复的规则评估。
| 变量 | 用途 | 默认值 |
|----------|---------|---------|
| `scan_yara` | 启用原生 YARA 扫描阶段:`auto`(检测二进制文件,回退到 ClamAV)、`0`(禁用)、`1`(启用) | `auto` |
| `scan_yara_timeout` | 超时时间(秒)(0=无超时) | `300` |
| `scan_yara_scope` | 当 ClamAV 同时启用时的规则范围:`all`(完整原生扫描)或 `custom`(仅原生扫描自定义规则,ClamAV 处理 rfxn.yara) | `custom` |
| `sig_import_yara_url` | 在签名更新时下载自定义 YARA 规则的 URL | — |
**自定义 YARA 规则**可以放在以下两个位置,两者在升级时都会保留:
- `sigs/custom.yara` — 单文件规则
- `sigs/custom.yara.d/` — 用于存放 `.yar` 和 `.yara` 规则文件的 drop-in 目录
兼容第三方规则集,例如 [YARA Forge](https://yarahq.github.io/) 和 [Signature Base](https://github.com/Neo23x0/signature-base)。也支持通过 `sigs/compiled.yarc` 使用编译后的规则(`yarac` 输出)。
**批量扫描:** YARA 4.0+ 及所有 YARA-X 版本使用 `--scan-list` 进行高效的批量文件扫描。旧版 YARA 会自动回退到逐文件扫描。
无需编辑配置即可在运行时启用:```bash
maldet -co scan_yara=1 -a /home/?/public_html
扫描完成后可调用可配置的钩子脚本,通过位置参数、LMD_* 环境变量以及(可选)stdin 上的 JSON 接收扫描结果。该钩子绝不会中断扫描 — 失败仅记录日志,并非致命。完整的钩子约定请参阅 maldet(1)。
启用 ClamAV 扫描时(scan_clamscan=1 或 auto 且检测到二进制文件),LMD 按优先级顺序选择最佳可用的 ClamAV 引擎:
clamdscan(如果 scan_clamd_remote=1 且存在配置文件)clamd 守护进程clamd 守护进程(使用 --fdpass)clamscan 二进制文件(回退方案,速度较慢)LMD 签名由 install.sh 自动符号链接到 ClamAV 数据目录,使 ClamAV 能够访问 LMD 的 MD5(rfxn.hdb)、HEX(rfxn.ndb)和 YARA(rfxn.yara)签名。
签名验证门控: 在将更新的签名部署到 ClamAV 数据目录之前,LMD 会通过 clamscan -d 在暂存目录中对它们进行验证。如果验证失败,现有的 LMD 签名将从 ClamAV 路径中移除,以防止格式错误的数据库破坏 ClamAV。仅当至少有一个 ClamAV 数据目录通过验证时,才会向 clamd 发送 SIGUSR2 重载信号(issue #467)。
| 变量 | 用途 | 默认值 |
|---|---|---|
scan_clamscan | 启用 ClamAV 作为扫描引擎:auto(运行时检测二进制文件)、0(禁用)、1(启用) | auto |
后面的来源会覆盖前面的值:
internals/internals.conf — 内部路径、二进制文件发现、URL 定义conf.maldet — 面向用户的配置internals/compat.conf — 已弃用的变量映射/etc/sysconfig/maldet 或 /etc/default/maldet — 系统覆盖-co|--config-option — 运行时覆盖命令行界面、退出代码和常见示例。完整的选项参考请参阅 man maldet(1)。```
usage: maldet [OPTION] [ARGUMENT]
SCANNING: -a, --scan-all PATH scan all files in path (wildcard: ?) -r, --scan-recent PATH DAYS scan files created/modified in last X days -f, --file-list FILE scan files from a line-separated file list -b, --background run scan in the background
SCAN FILTERS: -i, --include-regex REGEX include only matching paths -x, --exclude-regex REGEX exclude matching paths -co, --config-option V=V,... override config options at runtime -U, --user USER run as specified user
MONITORING: -m, --monitor USERS|PATHS|FILE|RELOAD start inotify monitoring -k, --kill-monitor stop inotify monitoring
SCAN MANAGEMENT: -L, --list-active list active scans (text/json/tsv) --kill SCANID abort a running scan --pause SCANID [DURATION] pause a running scan (e.g., 2h, 30m) --unpause SCANID resume a paused scan --stop SCANID checkpoint and stop a running scan --continue SCANID resume a stopped scan from checkpoint --maintenance rotate histories, compress/archive old sessions
QUARANTINE & RESTORE: -q, --quarantine SCANID quarantine hits from scan -n, --clean SCANID clean malware from scan hits -s, --restore FILE|SCANID restore quarantined file(s) -qd PATH override quarantine directory for this run
REPORTING: -e, --report [SCANID|list|latest|hooks|active] view scan report --all show full history with -e list (default: recent) --format text|json|html|tsv set report output format (default: text) --mailto ADDRESS email report to address --json-report [SCANID|list] shorthand: --report --format json --alert-daily generate inotify monitor digest alert --digest fire unified digest (monitor + hook sources) -l, --log view event log
UPDATES: -u, --update-sigs [--force] update malware signatures -d, --update-ver [--force|--beta] update LMD version --cron-sigup cron sig update (internal use)
OTHER: -p, --purge clear logs, quarantine, temp data -c, --checkout FILE submit suspected malware to rfxn.com --test-alert TYPE CHANNEL test alert delivery (scan|digest, email|slack|telegram|discord) --mkpubpaths create per-user pub/ data directories --web-proxy IP:PORT set HTTP/HTTPS proxy -hscan, --hook-scan scan via service hook (internal use) -v, --version show version information -h, --help show detailed help
### 4.1 退出代码
| 代码 | 含义 |
|------|---------|
| `0` | 成功,未发现恶意软件命中 |
| `1` | 错误或所有扫描路径不存在 |
| `2` | 发现恶意软件命中 |
**示例:**```bash
# Scan all files under user web roots
maldet -a /home/?/public_html
# Scan recent files with auto-quarantine and YARA enabled
maldet -co quarantine_hits=1,scan_yara=1 -r /home/?/public_html 2
# Background scan with email alert to specific address
maldet -b -co [email protected] -a /var/www
# View the most recent scan report
maldet -e
# Email a specific report
maldet --mailto [email protected] -e 050910-1534.21135
# Output scan report as JSON (pipe to jq for formatting)
maldet --format json -e 050910-1534.21135
# List all reports as JSON
maldet --format json -e list
# Shorthand JSON output (equivalent to --format json -e)
maldet --json-report 050910-1534.21135
# Restore all quarantined files from a scan
maldet -s 050910-1534.21135
JSON 输出: 使用 --format json -e 或 --json-report 输出结构化 JSON
(v1.2 schema),包含扫描器元数据、扫描详情(路径、时间、文件计数)、每个
命中条目,包括签名名称、文件路径、命中类型、所有者、权限和隔离
状态,以及按类型分类的摘要。同时支持 TSV 和旧版纯文本会话;
旧版会话包含 "source": "legacy",并将不可用的
富字段(hash、size、owner 等)渲染为 null。
大型文件系统(1M+ 文件)上的长时间运行扫描可以在不使用 kill -9 的情况下进行控制:```bash
maldet -L
maldet --format json -L
maldet --kill 260327-1509.25279
maldet --pause 260327-1509.25279 2h
maldet --unpause 260327-1509.25279
maldet --stop 260327-1509.25279
maldet --continue 260327-1509.25279
maldet --maintenance
**检查点恢复:** `--stop` 写入一个检查点文件,记录扫描阶段、命中
计数、配置选项和每个工作进程的进度。`--continue` 验证检查点,
如果签名发生变化则发出警告,根据当前文件系统状态重建文件列表,恢复
先前的命中,并从中断的阶段恢复扫描。HEX 工作进程以
块粒度恢复(每个工作进程损失约 30 秒的工作量)。
**引擎支持:** 原生引擎(HEX/CSIG/hash 工作进程)和独立的 `clamscan`
支持所有生命周期操作。基于守护进程的 ClamAV(`clamdscan`/`clamd`)仅支持
`--kill` — `--pause` 和 `--stop` 会被拒绝并返回错误消息,因为
守护进程的 I/O 状态是不透明的,且与其他客户端共享。
---
## 5. 忽略选项
从扫描和监控中排除路径、文件类型和签名。
四个忽略文件控制从扫描中排除的内容:
| 文件 | 格式 | 用途 |
|------|--------|---------|
| `ignore_paths` | 换行分隔的路径 | 从扫描中排除目录或文件 |
| `ignore_file_ext` | 换行分隔的扩展名 | 排除文件扩展名(`.js`、`.css`) |
| `ignore_sigs` | 换行分隔的模式 | 跳过匹配的签名(正则表达式、子字符串匹配) |
| `ignore_inotify` | 换行分隔的文本路径 | 排除 inotify 监控事件 |
所有忽略文件都位于 `/usr/local/maldetect/` 目录下。
**示例:**```
# ignore_paths
/home/user/public_html/cgi-bin
# ignore_file_ext
.js
.css
# ignore_sigs
base64.inject.unclassed
# ignore_inotify (user-owned — your additions only)
/home/user/public_html/cache/
backup-
# ignore_inotify.defaults (LMD-managed — refreshed on every upgrade)
# Ships under /usr/local/maldetect/internals/ with curated defaults
# for systemd-private tmpdirs, MariaDB temp tables, Redis, ClamAV, etc.
# Do NOT edit — add your own entries to ignore_inotify instead.
注意: ignore_sigs 条目被视为扩展正则表达式模式,并作为子字符串进行匹配。条目 php.shell 将抑制 php.shell、php.shell.v2、{YARA}php.shell.backdoor 等。使用 ^php\.shell$ 进行精确匹配。在正则表达式中,. 字符匹配任意字符;如需匹配字面点号,请将其转义为 \.。
自动每日扫描、数据清理与签名更新。
安装在 /etc/cron.daily/maldet 的 cron 任务执行三项操作:
cron_prune_days(默认:21)的隔离区、会话和临时数据autoupdate_signatures 和 autoupdate_version 时)scan_days 天内,默认:1)每日扫描会自动检测已安装的控制面板,并相应调整扫描路径:
如果监控模式已激活,则跳过每日扫描,改为发出监控事件的每日报告。
对于自定义扫描路径,请使用钩子文件 /usr/local/maldetect/cron/custom.cron。对于特定于 cron 的配置覆盖,请使用 /etc/sysconfig/maldet(RHEL)或 /etc/default/maldet(Debian)或 /usr/local/maldetect/cron/conf.maldet.cron。
每周看门狗脚本(/etc/cron.weekly/maldet-watchdog)在主 cron 损坏或过期时提供独立的备用签名更新。
基于内核 inotify 的实时文件监控、摘要警报和监督进程管理。
实时文件监控使用内核 inotify 子系统来检测文件的创建、修改和移动事件。需要内核启用 CONFIG_INOTIFY_USER(所有现代内核均标配)。```bash
maldet -m users
maldet -b -m users
maldet -m /home/mike,/home/ashton
maldet -m /root/monitor_paths
maldet -k
**工作原理:**
`maldet -m` 默认在前台运行一个监督进程。该监督进程管理一个 `inotifywait` 子进程,并处理崩溃恢复、配置重载和摘要告警。使用 `-b` 可将监督进程后台化并立即返回。
1. `monitor_init()` 在受监控路径下的所有文件上设置 inotify 监视
2. 文件事件会排队,并每隔 `inotify_sleep` 秒(默认:15)进行批量扫描
3. 配置每隔 `inotify_reloadtime` 秒(默认:3600)重载一次
4. 内核的 `max_user_watches` 和 `max_user_instances` 会自动调优以获得最佳性能
5. 如果 `inotifywait` 崩溃,监督进程会以指数退避(2、4、8、16、32 秒)将其重启;连续失败 3 次后,监督进程退出
**摘要告警**会按照 `digest_interval` 设置的间隔(默认:`24h`)定期发送。如果自上次摘要以来的命中数超过 `digest_escalate_hits`,则会立即发送升级告警(默认:`0`,即禁用)。
**附加路径:** 如需在不编辑 `conf.maldet` 的情况下监控更多路径,可将其逐行添加到 `monitor_paths_extra` 所引用的文件(默认:`/usr/local/maldetect/monitor_paths.extra`)中。每次配置重载时,这些路径都会与主监控列表合并。
使用 `users` 模式时,仅监控与 `inotify_docroot`(默认:`public_html,public_ftp`)匹配的子目录,以及系统临时目录 `/tmp`、`/var/tmp` 和 `/dev/shm`。
**忽略文件(inotify):** 监控排除规则为两个文件的并集——用户所有的 `/usr/local/maldetect/ignore_inotify` 和 LMD 管理的 `/usr/local/maldetect/internals/ignore_inotify.defaults`。两者均以换行分隔,支持 `#` 注释和空行。用户所有的 `ignore_inotify` 默认接受 POSIX ERE(扩展正则表达式)——锚点(`^`、`$`)和通配符(`.*`、`.+`)均有效。若要排除包含正则表达式元字符的字面路径,请在该条目前面加上 `literal:` 前缀(例如 `literal:/tmp/app.cache`)。LMD 管理的 `ignore_inotify.defaults` 按字面子串处理——其精选条目在加载时会被自动转义。该默认文件在每次 LMD 升级时都会被覆盖;站点特定的排除规则只能添加到 `ignore_inotify`。
---
## 8. 签名系统
签名类型、命名约定、更新及自定义规则文件。
LMD 随附五种签名类型:
| 类型 | 文件 | 格式 | 数量 |
|------|------|--------|-------|
| MD5 哈希 | `sigs/md5v2.dat` | `HASH:SIZE:{MD5}sig.name.N` | ~14,801 |
| SHA-256 哈希 | `sigs/sha256v2.dat` | `HASH:SIZE:{SHA256}sig.name.N` | CDN |
| HEX 模式 | `sigs/hex.dat` | `HEXSTRING:{HEX}sig.name.N` | ~2,054 |
| 复合签名 | `sigs/csig.dat` | `SUBSIG1\|\|SUBSIG2:signame` | CDN |
| YARA 规则 | `sigs/rfxn.yara` | YARA 语法 | ~783 条规则 |
| 编译后的 YARA | `sigs/compiled.yarc` | `yarac` 输出 | 可选 |
同时维护与 ClamAV 兼容的签名:
- `sigs/rfxn.hdb` — ClamAV MD5 格式
- `sigs/rfxn.ndb` — ClamAV HEX 格式
- `sigs/rfxn.hsb` — ClamAV SHA-256 格式(需要 ClamAV >= 0.97)
**签名命名约定:** `{TYPE}category.name.variant_number`
类别包括:`bin.`(二进制)、`c.`(C 语言)、`exp.`(漏洞利用)、`php.`(PHP)、`js.`(JavaScript)、`perl.`(Perl)、`html.`(网络钓鱼)、`base64.inject.`、`gzbase64.`
**扫描报告中的命中前缀:**
| 前缀 | 来源 |
|--------|--------|
| `{MD5}` | MD5 哈希匹配(阶段 1) |
| `{SHA256}` | SHA-256 哈希匹配(阶段 1) |
| `{HEX}` | HEX 模式匹配(阶段 2) |
| `{CSIG}` | 复合签名匹配(阶段 2.5) |
| `{SA}` | 统计分析(字符串长度) |
| `{YARA}` | 原生 YARA 扫描(`scan_yara=1`) |
| `{CAV}` | ClamAV 引擎(clamd/clamscan) |
### 8.1 签名更新
签名通过 cron 作业每日更新,也可手动更新:```bash
maldet -u # update signatures
maldet -u --force # force update even if current
自定义签名可以按三种格式添加,所有格式在升级后都会保留:
可以配置远程导入 URL,以便在签名更新期间自动下载:
隔离、恢复和清理受恶意软件感染的文件。
隔离文件存储在 /usr/local/maldetect/quarantine/ 目录下,权限设置为 000。原始路径、所有者、权限和修改时间记录在 /usr/local/maldetect/sess/quarantine.hist 中,以便完全恢复。```bash
maldet -q SCANID
maldet -s SCANID
maldet -s /usr/local/maldetect/quarantine/config.php.23754
maldet -n SCANID
**隔离文件命名:** `FILENAME.INODE`(例如 `config.php.23754`)
对于非 root 扫描(例如 ModSecurity2 上传扫描),隔离数据存储于 `/usr/local/maldetect/pub/USERNAME/quar/`。使用 `-U` 标志与非 root 隔离进行交互:```bash
maldet -U nobody -s 112012-0032.13771
清理器函数会在 clean/ 目录下查找以签名命名的脚本。每个脚本都会收到受感染文件路径作为参数,并应剥离恶意内容。清理后,文件会重新扫描——如果仍然触发命中,则该清理被标记为 FAILED。
要为签名 php.cmdshell.r57 创建清理规则,请添加一个文件 clean/php.cmdshell.r57,其中包含类似 sed -i 的命令并带有相应模式。成功清理会将文件恢复到其原始路径、所有者和权限。
清理器是隔离的一个子功能——文件必须先被隔离(或使用 -n),清理才会执行。
用于 ModSecurity、FTP、Exim 及自定义集成的服务 Hook API。
LMD 通过统一的 hookscan.sh API 为多个服务提供实时文件扫描。单个脚本可处理 ModSecurity、pure-ftpd、ProFTPD、Exim 以及通用(自定义)集成的模式分发。
Hook 扫描检测结果会记录到滚动命中日志(hook.hits.log),而不会创建每次扫描的会话文件。检测结果会包含在定期摘要警报中,并可通过 maldet --report hooks 查看。
SecRequestBodyAccess On
SecTmpSaveUploadedFiles On # Required for ModSecurity >= 2.9
SecRule FILES_TMPNAMES "@inspectFile /usr/local/maldetect/hookscan.sh"
"id:1999999,phase:2,t:none,deny,log,auditlog,severity:2,
msg:'Malware upload blocked by LMD'"
恶意上传将以 deny 操作被拒绝,并记录到 ModSecurity 审计日志中。无需提供模式参数 —— `hookscan.sh` 默认使用 ModSecurity 模式以实现向后兼容。ModSecurity v2 和 v3(libmodsecurity)均使用相同的 `popen()` 契约。
### 10.2 pure-ftpd```bash
# pure-ftpd.conf (or command-line flags):
CallUploadScript yes
# Start the upload-script daemon:
pure-uploadscript -r /usr/local/maldetect/hookscan.sh -B
需要编译时带有 --with-uploadscript 的 pure-ftpd。该模式通过 UPLOAD_VUSER 环境变量自动检测。受感染文件在上传后被隔离(即发即忘——上传无法被阻止,只能事后处理)。
av_scanner = cmdline:
/usr/local/maldetect/hookscan.sh exim %s :
maldet: (.+):
maldet: (.+)
三个以冒号分隔的字段分别是:命令模板、触发正则表达式和名称捕获正则表达式。检测到恶意软件时,Exim 会使用捕获的签名名称拒绝该消息。
### 10.5 通用 API
用于自定义集成、批量扫描和第三方使用:```bash
# Single file scan
hookscan.sh generic /path/to/file
# Exit: 0 = clean, 1 = error, 2 = infected
# Stdout: CLEAN: /path, INFECTED: signame /path, ERROR: reason
# Batch scan from a file list
hookscan.sh generic --list /tmp/filelist.txt
# Batch scan from stdin
find /uploads -newer /tmp/marker -type f | hookscan.sh generic --stdin
批量输出会为每个文件生成一行 STATUS: PATH。退出码为最差结果优先。
Hook 扫描配置存储在 conf.maldet.hookscan 中(可选 — 默认值已内置在脚本中)。参考默认值位于 conf.maldet.hookscan.default。
关键变量:
启用后运行 maldet --mkpubpaths 以创建用于非 root 扫描操作的每用户数据目录。
验证告警投递通道是否配置正确:```bash maldet --test-alert scan email # test per-scan email alert maldet --test-alert scan slack # test per-scan Slack alert maldet --test-alert digest email # test digest email alert maldet --test-alert digest telegram # test digest Telegram alert
测试警报使用真实渲染管道处理合成数据。主题行以 `[TEST]` 为前缀。通道隔离确保只有指定的通道触发。
### 10.8 Hook 摘要
Hook 检测通过周期性摘要警报进行汇总:```bash
# On-demand digest (reads all sources: monitor + hooks)
maldet --digest
# View hook scan activity
maldet --report hooks # last 24 hours
maldet --report hooks --last 7d # last 7 days
maldet --report hooks --mode modsec # filter by mode
当存在新的检测结果时,每日 cron 作业会自动触发钩子摘要(由 conf.maldet 中的 cron_digest_hook=1 控制)。
对于使用 LMD 替代 CXS 的管理员:
将 LMD 与外部工具、自动化流水线和第三方扫描器连接。
install.sh 会自动将 LMD 签名符号链接到 ClamAV 数据目录,从而提供双引擎覆盖。设置 scan_clamscan=auto(默认)即可自动检测 ClamAV。有关引擎选择和签名验证,请参阅 3.8 ClamAV 集成。
启用 enable_statistic=1 并配置 elk_host、elk_port 和 elk_index,即可将扫描事件流式传输到 Elasticsearch。请参阅 3.10 ELK 集成。
LMD 除电子邮件外还支持四种告警投递渠道:Slack(Block Kit)、Telegram(MarkdownV2)、Discord(webhook 嵌入)以及适用于没有本地 MTA 环境的 SMTP 中继。配置请参阅 3.2 告警。
面向 CI/CD 和自动化的机器可读扫描输出:```bash maldet --format json -e SCANID # JSON report to stdout maldet --json-report list # list all scans as JSON
请参阅 `man maldet`(1) 以了解 v1.2 JSON 模式(在 `-e SCANID` 和 `-e list` 两种用法下均采用统一的 `{schema_version, scanner, host, reports[]}` 结构)。
### 托管控制面板检测
每日 cron 任务会自动检测 12 种以上的托管控制面板并调整扫描路径。完整的面板矩阵请参见 [6. Cron Daily](#6-cron-daily)。
---
## 许可证
LMD 由 Ryan MacDonald [[email protected]] 以志愿方式开发和支持。
Linux Malware Detect (LMD) 在 GNU 通用公共许可证 (GPL) v2 下分发,对使用或再分发没有任何限制。版权声明和 GNU GPL 包含在 `COPYING.GPL` 文件中。根据 GNU GPL 的要求,衍生作品必须注明出处。
---
## 支持
LMD 源代码仓库位于:https://github.com/rfxn/linux-malware-detect
Bug、功能请求和一般问题可以提交为 GitHub issue,或发送至 [email protected]。
官方项目页面位于:https://www.rfxn.com/projects/linux-malware-detect/
| 变量 | 用途 | 默认值 |
|---|
quarantine_hits | 自动隔离检测到的恶意软件 | 0 |
quarantine_clean | 尝试从隔离文件中清除恶意软件 | 0 |
quarantine_suspend_user | 命中时暂停 cPanel 账户或撤销 shell | 0 |
quarantine_suspend_user_minuid | 可暂停的最低 UID(保护系统账户) | 500 |
quarantine_on_error | 扫描引擎返回错误时隔离文件 | 1 |
| 变量 | 用途 | 默认值 |
|---|
default_monitor_mode | 监控器启动模式(users 或文件路径);留空 = 禁用 | "" |
inotify_base_watches | 每个用户路径的基础文件监视数量 | 16384 |
inotify_minuid | 用户主目录监控的最低 UID | 500 |
inotify_docroot | 用户主目录中要监控的子目录 | public_html,public_ftp |
inotify_sleep | 扫描批次之间的间隔秒数 | 15 |
inotify_reloadtime | 配置重载之间的间隔秒数 | 3600 |
inotify_cpunice | 监控进程的 nice 优先级 | 18 |
inotify_ionice | 监控进程的 IO 优先级 | 6 |
inotify_cpulimit | 监控器的 CPU 硬限制(0=禁用) | 0 |
digest_interval | 定期摘要汇总告警的间隔:24h、30m、7d、0(禁用) | 24h |
digest_escalate_hits | 立即升级告警的命中数阈值;0 = 禁用 | 0 |
cron_digest_hook | 启用 cron.daily 钩子摘要扫描(若存在新的钩子检测结果则触发摘要) | 1 |
monitor_paths_extra | 指向按行分隔的附加 inotify 监视路径文件的路径 | /usr/local/maldetect/monitor_paths.extra |
monitor_scan_owner_filters | 在监控模式下应用 scan_ignore_root/scan_ignore_user/scan_ignore_group 所有权过滤器;0(默认)= 关闭 — 监控器扫描所有文件,不论所有者(恢复 1.6.6 语义,修复 issue #485);1 = 开启 — 应用所有权过滤器 | 0 |
| 变量 | 用途 | 默认值 |
|---|
post_scan_hook | 钩子脚本路径(root 所有,不可全局写入)。不能通过 -co 设置 | "" |
post_scan_hook_format | 输出层级:args、file、json(累积) | args |
post_scan_hook_exec | 执行模式:async(非阻塞)、sync(等待) | async |
post_scan_hook_timeout | 发送 SIGTERM 前的秒数(0=禁用,最小 5) | 60 |
post_scan_hook_on | 扫描类型过滤器:all、cli、digest | all |
post_scan_hook_min_hits | 触发所需的最少命中数(0=始终触发) | 1 |
| 变量 | 用途 | 默认值 |
|---|
scan_clamd_remote | 使用远程 clamd 服务器进行扫描 | 0 |
remote_clamd_config | 远程 clamd 配置文件的路径 | /etc/clamd.d/clamd.remote.conf |
remote_clamd_max_retry | 远程 clamd 失败时的最大重试次数 | 5 |
remote_clamd_retry_sleep | 重试之间的间隔秒数 | 3 |
| 变量 | 用途 | 默认值 |
|---|
enable_statistic | 启用 ELK 技术栈统计数据收集 | 0 |
elk_host | ELK 输入的 TCP 主机 | — |
elk_port | ELK 输入的 TCP 端口 | — |
elk_index | Elasticsearch 索引名称 | — |
| 面板 | 扫描路径 |
|---|
| cPanel | /home?/?/public_html/ (+ 附加域/子域文档根目录) |
| Plesk | /var/www/vhosts/?/ |
| DirectAdmin | /home?/?/domains/?/public_html/, /var/www/html/?/ |
| Ensim | /home/virtual/?/fst/var/www/html/ |
| ISPConfig | /var/www/clients/?/web?/web, …/subdomains, /var/www |
| Virtualmin | /home/?/public_html/, /home/?/domains/?/public_html/ |
| ISPmanager | /var/www/?/data/, /home/?/data/ |
| Froxlor | /var/customers/webs/ |
| Bitrix | /home/bitrix/www/, /home/bitrix/ext_www/?/ |
| VestaCP / HestiaCP | /home/?/web/?/public_html/ (+ public_shtml, tmp, private) |
| DTC | ${conf_hosting_path:-/var/www/sites}/?/?/subdomains/?/html/ |
| 类型 | 文件 | 格式 |
|---|
| 自定义 MD5 | sigs/custom.md5.dat | 与 md5v2.dat 相同 |
| 自定义 SHA-256 | sigs/custom.sha256.dat | 与 sha256v2.dat 相同 |
| 自定义 HEX | sigs/custom.hex.dat | 与 hex.dat 相同 |
| 自定义 CSIG | sigs/custom.csig.dat | 与 csig.dat 相同 |
| 自定义 YARA | sigs/custom.yara | YARA 规则语法 |
| 自定义 YARA(直接放入) | sigs/custom.yara.d/*.yar | YARA 规则文件 |
| 已编译的 YARA | sigs/compiled.yarc | yarac 输出(可选) |
| 变量 | 用途 |
|---|
sig_import_md5_url | 自定义 MD5 签名的 URL |
sig_import_sha256_url | 自定义 SHA-256 签名的 URL |
sig_import_hex_url | 自定义 HEX 签名的 URL |
sig_import_csig_url | 自定义复合签名的 URL |
sig_import_yara_url | 自定义 YARA 规则的 URL |
| 变量 | 默认值 | 描述 |
|---|
hookscan_timeout | 30 | 扫描超时时间(秒) |
hookscan_fail_open | 1 | 扫描出错时放行文件(0 = 阻止) |
hookscan_escalate_hits | 0 | N 次 hook 命中/小时时立即告警(0 = 禁用) |
hookscan_service_users | apache,nginx,... | 不受家目录限制的服务 UID |
hookscan_user_rate_limit | 60 | 非 root 调用者的最大扫描次数/小时 |
hookscan_user_show_signames | 1 | 向非 root 调用者显示签名名称 |
hookscan_list_max_bytes | 1048576 | 列表文件最大大小(1 MB) |
hookscan_list_max_entries | 10000 | 文件列表中的最大条目数 |
| CXS 组件 | LMD 对应项 |
|---|
cxscgi.sh | hookscan.sh modsec |
cxsftp.sh | hookscan.sh ftp |
| ProFTPD 通过 mod_exec 调用 cxs | hookscan.sh proftpd |
cxs --file | hookscan.sh generic |
cxswatch | maldet --monitor |
/etc/cxs/cxs.conf | conf.maldet.hookscan |