Skip to main content
rfxn
LMDGPL v2v1.6.6.1Since 2005README

Linux Malware Detect

Malware scanner built from real hosting threat data

Stars

1,370

Forks

246

Last Push

Feb 25, 2026

Latest Release
1.6.6.1

Linux Malware Detect (LMD)

Malware scanner for Linux — multi-stage threat detection (MD5, HEX, YARA, statistical analysis), ClamAV integration, real-time inotify monitoring, quarantine/clean/restore operations, and multi-channel alerting (email, Slack, Telegram).

(C) 2002-2026, R-fx Networks <proj@rfxn.com>
(C) 2026, Ryan MacDonald <ryan@rfxn.com>
Licensed under GNU GPL v2


Contents


Quick Start

# Install to /usr/local/maldetect
./install.sh

# Scan all files under a path
maldet -a /home/?/public_html

# Scan files modified in the last 2 days
maldet -r /home/?/public_html 2

# Enable YARA scanning at runtime
maldet -co scan_yara=1 -a /home/?/public_html

# Quarantine all hits from a scan
maldet -q SCANID

# Start real-time inotify monitoring
maldet -m users

# Update signatures
maldet -u

1. Introduction

Linux Malware Detect (LMD) is a malware scanner for Linux released under the GNU GPLv2 license, designed around the threats faced in shared hosted environments. It uses threat data from network edge intrusion detection systems to extract malware that is actively being used in attacks and generates signatures for detection. In addition, threat data is derived from user submissions with the LMD checkout feature and from malware community resources.

LMD focuses on the malware classes that traditional AV products frequently miss: PHP shells, JavaScript injectors, base64-encoded backdoors, IRC bots, and other web-application-layer threats that target shared hosting user accounts rather than operating system internals.

Detection Stages

  • MD5 file hash matching for exact threat identification
  • HEX pattern matching for identifying threat variants and families
  • Native YARA rule scanning with full module support and custom rules
  • Statistical string-length analysis for detecting obfuscated threats (base64, gzinflate)
  • ClamAV integration for extended coverage with LMD-maintained ClamAV signatures

Scanning & Monitoring

  • Scan all files, recently modified files, or files from a list
  • Kernel inotify real-time file monitoring (create/modify/move events)
  • HTTP upload scanning via ModSecurity2 inspectFile hook
  • Background scanning for unattended large-scale operations
  • Per-scan include/exclude regex filtering

Quarantine & Response

  • Quarantine queue with zero-permission file storage
  • Batch quarantine/restore by scan ID
  • Signature-specific cleaner rules for malware removal
  • Full file restoration (content, owner, permissions, mtime)

Alerting & Reporting

  • Email alerts per scan or daily digest
  • Slack alerting via Bot API (files.getUploadURLExternal)
  • Telegram alerting via Bot API (sendDocument)
  • Scan reports with per-file hit details

Infrastructure

  • Automatic ClamAV signature linking for dual-engine coverage
  • Daily cron with auto-detection of 12+ hosting control panels
  • CPU/IO resource control (nice, ionice, cpulimit)
  • Signature and version auto-updates
  • systemd service unit and SysV init script support

Supported Platforms

LMD runs on any Linux distribution with bash and standard GNU utilities. Tested platforms:

PlatformInit SystemPackage Config Path
RHEL / Rocky / AlmaLinux 8, 9, 10systemd/etc/sysconfig/maldet
CentOS 6, 7SysV / systemd/etc/sysconfig/maldet
Debian 10, 11, 12systemd/etc/default/maldet
Ubuntu 20.04, 22.04, 24.04systemd/etc/default/maldet
GentooOpenRC
SlackwareSysV
FreeBSD— (partial; no inotify)

2. Installation

The included install.sh script handles all installation tasks. Previous installations are automatically backed up.

./install.sh

The installer:

  • Copies files to /usr/local/maldetect
  • Creates the maldet symlink in /usr/local/sbin/
  • Installs the cron.daily script to /etc/cron.daily/maldet
  • Installs the systemd service unit (or SysV init script on older systems)
  • Links LMD signatures to ClamAV data directories (if ClamAV is installed)
  • Preserves existing configuration (conf.maldet), custom signatures, and ignore files across upgrades

Previous installs are saved to /usr/local/maldetect.bk{PID} with a maldetect.last symlink to the most recent backup.

Default paths:

  • Install path: /usr/local/maldetect
  • Binary symlink: /usr/local/sbin/maldet
  • Cron script: /etc/cron.daily/maldet
  • Service unit: /usr/lib/systemd/system/maldet.service

3. Configuration

The main configuration file is /usr/local/maldetect/conf.maldet. All options are commented for ease of configuration. Options use 0/1 for disable/enable unless otherwise noted.

Configuration can also be overridden at runtime using the -co flag:

maldet -co quarantine_hits=1,email_addr=you@domain.com -a /home

3.1 General Options

VariablePurposeDefault
autoupdate_signaturesAuto-update signatures daily via cron1
autoupdate_versionAuto-update LMD version daily via cron1
autoupdate_version_hashedVerify LMD executable MD5 against upstream1
cron_prune_daysDays to retain quarantine/session/temp data21
cron_daily_scanEnable daily automatic scanning via cron1
import_config_urlURL to download remote configuration override
import_config_expireCache expiry for imported config (seconds)43200
import_custsigs_md5_urlURL to download custom MD5 signatures
import_custsigs_hex_urlURL to download custom HEX signatures
import_custsigs_yara_urlURL to download custom YARA rules

3.2 Alerting

VariablePurposeDefault
email_alertEnable email alerts after scans0
email_addrAlert recipient addressyou@domain.com
email_subjEmail subject line templatemaldet alert from $(hostname)
email_ignore_cleanSuppress alerts when all hits were cleaned1
email_panel_user_alertsSend panel user alerts on hit detection0
email_panel_fromFrom header for panel user alertsyou@example.com
email_panel_replytoReply-To header for panel user alertsyou@example.com
email_panel_alert_subjSubject line for panel user alertsmaldet alert from $(hostname)
slack_alertEnable Slack file upload alerts0
slack_subjFile name for Slack uploadmaldet alert from $(hostname)
slack_tokenSlack Bot API token (scopes: files:write, files:read)
slack_channelsComma-separated list of channel names or IDsmaldetreports
telegram_alertEnable Telegram alerts0
telegram_file_captionCaption for Telegram report filemaldet alert from $(hostname)
telegram_bot_tokenTelegram Bot API token
telegram_channel_idTelegram chat or group ID

3.3 Scanning Options

VariablePurposeDefault
scan_max_depthMaximum directory depth for find15
scan_min_filesizeMinimum file size to scan24 bytes
scan_max_filesizeMaximum file size to scan2048k
scan_hexdepthByte depth for HEX signature matching65536
scan_hexfifoUse FIFO-based HEX scanner (faster)1
scan_hexfifo_depthByte depth for FIFO HEX scanner524288
scan_cpuniceNice priority for scan process (0-19)19
scan_ioniceIO scheduling class priority (0-7)6
scan_cpulimitHard CPU limit percentage (0=disabled)0
scan_ignore_rootSkip root-owned files in scans1
scan_ignore_userSkip files owned by specific users
scan_ignore_groupSkip files owned by specific groups
scan_user_accessAllow non-root users to run scans0
scan_find_timeoutTimeout for find file list generation (0=disabled)0
scan_export_filelistSave find results to tmp/find_results.last0
scan_tmpdir_pathsWorld-writable temp paths included in -a/-r scans/tmp /var/tmp /dev/shm /var/fcgi_ipc
string_length_scanEnable statistical string-length analysis0
string_lengthMinimum suspicious string length150000

3.4 YARA Scanning

Native YARA scanning invokes the yara binary (or yr from YARA-X) independently of ClamAV, supporting full YARA modules, compiled rules, and custom rule files that ClamAV's limited YARA subset cannot handle. When both are available, yr (YARA-X) is preferred.

VariablePurposeDefault
scan_yaraEnable native YARA scan stage0
scan_yara_timeoutTimeout in seconds (0=no timeout)300
scan_yara_scopeRule scope when ClamAV is also active: all (full native scan) or custom (only custom rules natively, ClamAV handles rfxn.yara)custom
import_custsigs_yara_urlURL to download custom YARA rules on signature update

Custom YARA rules can be placed in two locations, both preserved across upgrades:

  • sigs/custom.yara — single-file rules
  • sigs/custom.yara.d/ — drop-in directory for .yar and .yara rule files

Compatible with third-party rule sets such as YARA Forge and Signature Base. Compiled rules (yarac output) are also supported via sigs/compiled.yarc.

Batch scanning: YARA 4.0+ and all YARA-X versions use --scan-list for efficient batch file scanning. Older YARA versions fall back to per-file scanning automatically.

Enable at runtime without editing config:

maldet -co scan_yara=1 -a /home/?/public_html

3.5 Quarantine Options

VariablePurposeDefault
quarantine_hitsAutomatically quarantine detected malware0
quarantine_cleanTry to clean malware from quarantined files0
quarantine_suspend_userSuspend cPanel account or revoke shell on hit0
quarantine_suspend_user_minuidMinimum UID to suspend (protects system accounts)500
quarantine_on_errorQuarantine files when scan engine returns error1

3.6 Monitoring Options

VariablePurposeDefault
default_monitor_modeStartup mode for monitor (users or path to file)users
inotify_base_watchesBase number of file watches per user path16384
inotify_minuidMinimum UID for user home monitoring500
inotify_docrootSubdirectories to monitor in user homespublic_html,public_ftp
inotify_sleepSeconds between scan batches15
inotify_reloadtimeSeconds between config reloads3600
inotify_cpuniceNice priority for monitor process18
inotify_ioniceIO priority for monitor process6
inotify_cpulimitHard CPU limit for monitor (0=disabled)0
inotify_verboseLog every file scanned (debug only)0

3.7 ClamAV Integration

When scan_clamscan=1, LMD selects the best available ClamAV engine in priority order:

  1. Remote clamdscan (if scan_clamd_remote=1 and config exists)
  2. Local clamd daemon running as root
  3. Local clamd daemon running as non-root (with --fdpass)
  4. clamscan binary (fallback, slower)

LMD signatures are automatically symlinked to ClamAV data directories by install.sh, giving ClamAV access to LMD's MD5 (rfxn.hdb), HEX (rfxn.ndb), and YARA (rfxn.yara) signatures.

VariablePurposeDefault
scan_clamscanEnable ClamAV as scan engine1

3.8 Remote ClamAV

VariablePurposeDefault
scan_clamd_remoteUse a remote clamd server for scanning0
remote_clamd_configPath to remote clamd config file/etc/clamd.d/clamd.remote.conf
remote_clamd_max_retryMax retries on remote clamd failure5
remote_clamd_retry_sleepSeconds between retries3

3.9 ELK Integration

VariablePurposeDefault
enable_statisticEnable ELK stack statistics collection0
elk_hostTCP host for ELK input
elk_portTCP port for ELK input
elk_indexElasticsearch index name

3.10 Configuration Loading Order

Later sources override earlier values:

  1. internals/internals.conf — internal paths, binary discovery, URL definitions
  2. conf.maldet — user-facing configuration
  3. internals/compat.conf — deprecated variable mappings
  4. /etc/sysconfig/maldet or /etc/default/maldet — system overrides
  5. CLI -co|--config-option — runtime overrides

4. CLI Usage

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  start inotify real-time monitoring
  -k, --kill-monitor            stop inotify monitoring

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)

REPORTING:
  -e, --report [SCANID] [email] view or email scan report
  -E, --dump-report SCANID      dump report to stdout
  -l, --log                     view event log

UPDATES:
  -u, --update-sigs [--force]   update malware signatures
  -d, --update-ver [--force|--beta]  update LMD version

OTHER:
  -p, --purge                   clear logs, quarantine, temp data
  -c, --checkout FILE           submit suspected malware to rfxn.com
  --web-proxy IP:PORT           set HTTP/HTTPS proxy
  -h, --help                    show detailed help

Exit codes: 0 = success / no hits, 1 = error, 2 = malware hits found.

Examples:

# 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_addr=admin@example.com -a /var/www

# View the most recent scan report
maldet -e

# Email a specific report
maldet -e 050910-1534.21135 admin@example.com

# Restore all quarantined files from a scan
maldet -s 050910-1534.21135

5. Ignore Options

Four ignore files control what is excluded from scanning:

FileFormatPurpose
ignore_pathsLine-separated pathsExclude directories or files from scans
ignore_file_extLine-separated extensionsExclude file extensions (.js, .css)
ignore_sigsLine-separated patternsSkip matching signatures (regex, substring match)
ignore_inotifyLine-separated regex patternsExclude inotify monitoring events

All ignore files are located under /usr/local/maldetect/.

Examples:

# ignore_paths
/home/user/public_html/cgi-bin

# ignore_file_ext
.js
.css

# ignore_sigs
base64.inject.unclassed

# ignore_inotify
^/home/user$
^/var/tmp/#sql_.*\.MYD$

Note: ignore_sigs entries are treated as extended regex patterns and match as substrings. An entry php.shell will suppress php.shell, php.shell.v2, {YARA}php.shell.backdoor, etc. Use ^php\.shell$ for an exact match. The . character matches any character in regex; escape it as \. for a literal dot.


6. Cron Daily

The cron job installed at /etc/cron.daily/maldet performs three tasks:

  1. Prune quarantine, session, and temp data older than cron_prune_days (default: 21)
  2. Update signatures and version (when autoupdate_signatures and autoupdate_version are enabled)
  3. Scan recently modified files under detected hosting panel paths

The daily scan auto-detects installed control panels and adjusts scan paths accordingly:

PanelScan Path
cPanel/home?/?/public_html/ (+ addon/subdomain docroots)
Plesk/var/www/vhosts/?/
DirectAdmin/home?/?/domains/?/public_html/
Ensim/home/virtual/?/fst/var/www/html/
ISPConfig/var/www/clients/?/web?/web
Virtualmin/home/?/public_html/
ISPmanager/var/www/?/data/
Froxlor/var/customers/webs/
Bitrix/home/bitrix/www/, /home/bitrix/ext_www/?/
VestaCP / HestiaCP/home/?/web/?/public_html/
DTC${conf_hosting_path}/

If monitor mode is active, daily scans are skipped and a daily report of monitoring events is issued instead.

For custom scan paths, use the hook file /usr/local/maldetect/cron/custom.cron. For configuration overrides specific to cron, use /etc/sysconfig/maldet (RHEL) or /etc/default/maldet (Debian) or /usr/local/maldetect/cron/conf.maldet.cron.

A weekly watchdog script (/etc/cron.weekly/maldet-watchdog) provides independent fallback signature updates when the primary cron is broken or stale.


7. Inotify Monitoring

Real-time file monitoring uses the kernel inotify subsystem to detect file creation, modification, and move events. Requires a kernel with CONFIG_INOTIFY_USER (standard on all modern kernels).

# Monitor all user home directories (UIDs >= inotify_minuid)
maldet -m users

# Monitor specific paths
maldet -m /home/mike,/home/ashton

# Monitor paths from a file
maldet -m /root/monitor_paths

# Stop monitoring
maldet -k

How it works:

  1. monitor_init() sets up inotify watches on all files under monitored paths
  2. File events are queued and batch-scanned every inotify_sleep seconds (default: 15)
  3. Configuration is reloaded every inotify_reloadtime seconds (default: 3600)
  4. Kernel max_user_watches and max_user_instances are auto-tuned for optimal performance

When using the users mode, only subdirectories matching inotify_docroot (default: public_html,public_ftp) are monitored, plus the system temp directories /tmp, /var/tmp, and /dev/shm.

Alerting in monitor mode uses daily digest reports via the cron job rather than per-file alerts.


8. Signature System

LMD ships with three signature types:

TypeFileFormatCount
MD5 hashessigs/md5v2.datHASH:SIZE:{MD5}sig.name.N~14,801
HEX patternssigs/hex.datHEXSTRING:{HEX}sig.name.N~2,054
YARA rulessigs/rfxn.yaraYARA syntax~783 rules
Compiled YARAsigs/compiled.yarcyarac outputoptional

ClamAV-compatible signatures are also maintained:

  • sigs/rfxn.hdb — ClamAV MD5 format
  • sigs/rfxn.ndb — ClamAV HEX format

Signature naming convention: {TYPE}category.name.variant_number

Categories include: bin. (binary), c. (C language), exp. (exploit), php. (PHP), js. (JavaScript), perl. (Perl), html. (phishing), base64.inject., gzbase64.

Hit prefixes in scan reports:

PrefixSource
{MD5}MD5 hash match (stage 1)
{HEX}HEX pattern match (stage 2)
{SA}Statistical analysis (string length)
{YARA}Native YARA scan (scan_yara=1)
{CAV}ClamAV engine (clamd/clamscan)

8.1 Signature Updates

Signatures are updated daily via the cron job or manually:

maldet -u            # update signatures
maldet -u --force    # force update even if current

8.2 Custom Signatures

Custom signatures can be added in three formats, all preserved across upgrades:

TypeFileFormat
Custom MD5sigs/custom.md5.datSame as md5v2.dat
Custom HEXsigs/custom.hex.datSame as hex.dat
Custom YARAsigs/custom.yaraYARA rule syntax
Custom YARA (drop-in)sigs/custom.yara.d/*.yarYARA rule files
Compiled YARAsigs/compiled.yarcyarac output (optional)

Remote import URLs can be configured for automatic download during signature updates:

VariablePurpose
import_custsigs_md5_urlURL for custom MD5 signatures
import_custsigs_hex_urlURL for custom HEX signatures
import_custsigs_yara_urlURL for custom YARA rules

9. Quarantine & Cleaning

Quarantined files are stored under /usr/local/maldetect/quarantine/ with permissions set to 000. Original path, owner, permissions, and modification time are recorded in quarantine.hist for full restoration.

# Quarantine all hits from a scan
maldet -q SCANID

# Restore all quarantined files from a scan
maldet -s SCANID

# Restore a specific file
maldet -s /usr/local/maldetect/quarantine/config.php.23754

# Clean (attempt malware removal) from a scan
maldet -n SCANID

Quarantine file naming: YYYYMMDD-HH-SIGNATURE-SCANPID.INODE

For non-root scans (e.g., ModSecurity2 upload scanning), quarantine data is stored under /usr/local/maldetect/pub/USERNAME/quar/. Use the -U flag to interact with non-root quarantine:

maldet -U nobody -s 112012-0032.13771

9.1 Cleaner Rules

The cleaner function looks for signature-named scripts under the clean/ directory. Each script receives the infected file path as an argument and should strip the malicious content. After cleaning, the file is rescanned — if it still triggers a hit, the clean is marked FAILED.

To create a clean rule for signature php.cmdshell.r57, add a file clean/php.cmdshell.r57 containing a command like sed -i with the appropriate pattern. Successful cleans restore the file to its original path, owner, and permissions.

The cleaner is a sub-function of quarantine — files must be quarantined (or use -n) for cleaning to execute.


10. ModSecurity2 Upload Scanning

LMD integrates with ModSecurity2's inspectFile hook for real-time HTTP upload scanning via the included hookscan.sh script.

Setup:

  1. Enable user access scanning in conf.maldet:

    scan_user_access=1
    
  2. Add to your ModSecurity2 configuration:

    SecRequestBodyAccess On
    SecRule FILES_TMPNAMES "@inspectFile /usr/local/maldetect/hookscan.sh" \
        "id:'999999',log,auditlog,deny,severity:2,phase:2,t:none"
    

    For ModSecurity >= 2.9, add SecTmpSaveUploadedFiles On before the rule.

  3. Restart Apache.

Malicious uploads are rejected with a 406 status code and logged to the ModSecurity audit log. The default scan options prioritize performance over accuracy (native engine only, no ClamAV, quarantine enabled). Edit hookscan.sh to customize. To enable YARA scanning for uploads, set scan_yara=1 in conf.maldet.hookscan (separate from the main conf.maldet).

Run maldet --mkpubpaths after enabling to create per-user data directories for non-root scan operations.


11. License

LMD is developed and supported on a volunteer basis by Ryan MacDonald [ryan@rfxn.com].

Linux Malware Detect (LMD) is distributed under the GNU General Public License (GPL) v2 without restrictions on usage or redistribution. The copyright statement and GNU GPL are included in the COPYING.GPL file. Credit must be given for derivative works as required under GNU GPL.


12. Support Information

The LMD source repository is at: https://github.com/rfxn/linux-malware-detect

Bugs, feature requests, and general questions can be filed as GitHub issues or sent to proj@rfxn.com.

The official project page is at: https://www.rfxn.com/projects/linux-malware-detect/