🌐 Language / Idioma: English (current) | Español
Complete toolkit for detecting, analyzing, and remediating PHP malware on Linux web hosting servers (CWP/cPanel/aaPanel) with WordPress. Includes forensic auditing, post-compromise hardening, and executive HTML report generation.
| Script | Description | Action |
|---|---|---|
malware_scanner.py |
Main heuristic engine — 6 signature layers, scoring, entropy | Scan, quarantine, clean, disable |
remediate.sh |
Pipeline orchestrator — chains 7 remediation phases | Runs full cycle automatically |
wp_backup_db.sh |
WordPress database backup | Dump + gzip per wp-config.php |
wp_db_scan.sh |
Database scanning — injections, spam, malicious admins | Detects and cleans transients |
cron_check.sh |
Malicious crontab detection (16 patterns) | Detects and disables |
wp_reemplaza.sh |
WP core restoration via rsync from clean copy | Backup + rsync + permissions |
fix_permissions.sh |
Hosting permission correction (auto-detects group) | 755/644/600/711 |
malware_scan.sh |
Quick triage with grep/find (report only) | Read-only |
wp_security_scan.sh |
Basic quarantine by grep signatures | Moves files |
wp_reinfection_audit.sh |
Reinfection vector audit — 52 checks in 10 categories | Read-only, generates TXT + JSON |
| Script | Description | Action |
|---|---|---|
cwp_forensic_audit.sh |
CWP server forensic audit — 27 analysis sections | Read-only, generates TXT + JSON report |
cwp_harden.sh |
Post-compromise hardening for CWP — 7 correction phases | Applies fixes with --dry-run available |
| Script | Description | Action |
|---|---|---|
aapanel_forensic_audit.sh |
aaPanel server forensic audit — 28 analysis sections | Read-only, generates TXT + JSON report |
aapanel_remediate.sh |
Remediation pipeline for aaPanel — 6 phases | Full cycle with --clean/--disable |
| Script | Description | Action |
|---|---|---|
generate_html_report.py |
Executive HTML report generator from scanner JSON | Generates HTML with charts and recommendations |
malware_watcher.py |
Real-time resident antivirus daemon — monitors /home with inotify | Alerts on each PHP change, no polling |
install_watcher.sh |
Installer/uninstaller for the malware_watcher daemon | Install, uninstall, update, status |
mkdir -p /home_D0/fixes/logs /home_D0/fixes/quarantine /home_D0/fixes/backups./remediate.sh /home./remediate.sh --clean /homeHeuristic detection engine with 6 signature layers, entropy analysis, intelligent scoring, and false positive deduction.
Options:
| Option | Description |
|---|---|
--scan DIR |
Directory to scan (default: /home) |
--clean |
Clean simple injections with regex |
--disable |
Disable malware: chmod 000 + rename .DISABLED |
--no-quarantine |
Report only, don't move or touch files |
--report FMT |
Format: text, json, both (default: both) |
--threshold N |
Minimum score to report (default: 60) |
--validate-wp |
Validate WordPress core integrity |
--verify |
Re-scan after cleanup |
--workers N |
Parallel workers (default: 0 = sequential) |
--download-wp [VER] |
Download clean WP from wordpress.org |
Action Modes:
| Mode | Command | What it does |
|---|---|---|
| Report only | --no-quarantine |
Scans and generates reports without touching files |
| Quarantine | (default) | Moves malware to /home_D0/fixes/quarantine/ |
| Cleanup | --clean |
Removes injections with regex, quarantines if fails |
| Disabled | --disable |
chmod 000 + .DISABLED, log per user |
Scoring System:
| Threshold | Verdict | Meaning |
|---|---|---|
| < 60 | CLEAN | No action |
| ≥ 60 | SUSPICIOUS | Investigate context |
| ≥ 80 | LIKELY_MALWARE | Auto-cleanable |
| ≥ 90 | MALWARE | High confidence, act |
Examples:
# Report only, don't touch files
python3 malware_scanner.py --scan /home --no-quarantine
# Scan a specific site
python3 malware_scanner.py --scan /home/user/public_html --no-quarantine
# Clean injections and verify result
python3 malware_scanner.py --scan /home --clean --verify
# Disable malware in-place
python3 malware_scanner.py --scan /home --disable
# Validate WordPress core integrity
python3 malware_scanner.py --scan /home --validate-wp --report both
# Lower threshold (more sensitive, more findings)
python3 malware_scanner.py --scan /home --threshold 40
# Parallel scanning with 4 workers
python3 malware_scanner.py --scan /home --workers 4
# Download clean WP and validate core
python3 malware_scanner.py --scan /home --download-wp --validate-wp
# Download specific WP version
python3 malware_scanner.py --scan /home --download-wp 6.4.2 --validate-wp
# Aggressive scan: low threshold, parallel, cleanup + verification
python3 malware_scanner.py --scan /home --threshold 40 --workers 8 --clean --verifyReports generated in /home_D0/fixes/logs/:
| File | Content |
|---|---|
scan_*.log |
Human-readable log with matches, hashes, scores |
scan_*.json |
Structured JSON with findings, stats, verdicts |
scan_*_files.txt |
Quick triage: MALWARE/LIKELY/SUSPICIOUS by score |
scan_*.rm_ALL.txt |
Bash script with all rm commands |
scan_*.rm_<SIG>.txt |
One script per detection type |
Chains the 7 remediation phases in a single command.
Options:
| Option | Description |
|---|---|
--clean |
Activate regex cleanup |
--disable |
Activate disable mode (chmod 000 + .DISABLED) |
--dry-run |
Report only, don't execute actions |
--skip-restore |
Skip WP core restoration |
--threshold N |
Minimum score (default: 60) |
--workers N |
Parallel workers (default: 0) |
Pipeline Phases:
| Phase | Description | When executed |
|---|---|---|
| 1 | Database backup | Always |
| 2 | Heuristic scan (report only) | Always |
| 2b | Database scan | Always |
| 2c | Crontab verification | Always |
| 3 | Findings classification | Always |
| 4 | Malware cleanup + DB transients | Only with --clean/--disable |
| 5 | WordPress core restoration | Only with --clean/--disable |
| 6 | Verification re-scan | Only with --clean/--disable |
| 7 | Permission correction | Only with --clean/--disable |
Examples:
# Full diagnostic (no changes)
./remediate.sh /home
# A specific site
./remediate.sh /home/user/public_html
# Full pipeline with cleanup
./remediate.sh --clean /home
# In-place disable (safer than --clean)
./remediate.sh --disable /home
# Dry-run: see what it would do without executing
./remediate.sh --dry-run /home
# Cleanup without restoring WP core
./remediate.sh --clean --skip-restore /home
# Parallel with low threshold
./remediate.sh --workers 4 --threshold 40 /homeFinds all wp-config.php files, extracts DB credentials, and runs mysqldump + gzip.
Options:
| Option | Description |
|---|---|
[directory] |
Path to search (default: /home) |
Examples:
# Backup all databases in /home
./wp_backup_db.sh
# Single site only
./wp_backup_db.sh /home/user/public_html
# Backup + scan
./wp_backup_db.sh /home && python3 malware_scanner.py --scan /home --no-quarantineBackups in: /home_D0/fixes/backups/db_<slug>_<timestamp>.sql.gz
Detects malware injections in WordPress databases.
Options:
| Option | Description |
|---|---|
--fix |
Delete malicious transients from DB |
[directory] |
Path to search (default: /home) |
What it detects:
wp_options— siteurl/home modified to external domainswp_options— options witheval(),base64_decode(),<script>,<iframe>wp_options— transients with malicious contentwp_posts— posts with SEO spam, hidden iframes, injected scriptswp_users— admins created in the last 30 days with disposable emails
Examples:
# Scan all databases (report only)
./wp_db_scan.sh
# Scan a single site
./wp_db_scan.sh /home/user/public_html
# Scan and clean malicious transients
./wp_db_scan.sh --fix /home
# Backup first, then scan DB
./wp_backup_db.sh /home && ./wp_db_scan.sh /homeDetects crontabs that reinstall malware (reinfection cause #1).
Options:
| Option | Description |
|---|---|
--fix |
Comment out malicious lines (prefix # DISABLED_BY_CRON_CHECK:) |
[directory] |
Base path to find users (default: /home) |
Locations checked:
crontab -l -u <user>— user crontab/var/spool/cron/<user>— CentOS/RHEL/var/spool/cron/crontabs/<user>— Debian/Ubuntu/etc/cron.d/*— system crontabs/etc/cron.hourly/,daily/,weekly/,monthly//etc/crontab
Suspicious patterns (16): wget/curl with flags, python/php/perl inline, execution from /tmp/ or /dev/shm/, base64 decode, pipe to bash/sh, chmod 777, echo to .php.
Examples:
# Check all users (report only)
./cron_check.sh
# Check a single user
./cron_check.sh /home/user
# Detect and disable malicious crontabs
./cron_check.sh --fix
# Clean crontabs BEFORE cleaning files (important)
./cron_check.sh --fix && python3 malware_scanner.py --scan /home --cleanRestores wp-admin/, wp-includes/, and root .php files using rsync from a clean copy.
Requirement: Clean WordPress copy in /home_D0/fixes/wordpress/
# Prepare clean copy
wget https://wordpress.org/latest.tar.gz
tar xzf latest.tar.gz -C /home_D0/fixes/
# Or with malware_scanner.py
python3 malware_scanner.py --download-wp --scan /dev/null --no-quarantineWhat it does per site:
- Finds
wp-config.phpin/homerecursively - Validates WP structure, detects Multisite, compares versions
- DB backup (
mysqldump + gzip) - Backup of current core (
tar.gz) rsync -a --checksum --delete(excludeswp-contentandwp-config.php)- Fixes permissions and ownership
What it does NOT touch: wp-content/, wp-config.php, the database.
Examples:
# Repair all WP installations in /home
./wp_reemplaza.sh
# Backup DB first (recommended)
./wp_backup_db.sh /home && ./wp_reemplaza.sh
# Restore core + fix permissions
./wp_reemplaza.sh && ./fix_permissions.sh --allFixes permissions and ownership for hosting accounts. Compatible with CWP and cPanel.
Options:
| Option | Description |
|---|---|
-a, --all |
Process all accounts in /home |
-u, --user USER |
Process only this user (repeatable) |
-n, --dry-run |
Show without executing changes |
-v, --verbose |
Detailed output |
Permissions applied:
| Resource | Permissions |
|---|---|
| Directories | 755 |
| Files | 644 |
wp-config.php |
600 |
.htaccess |
644 |
/home/user (base) |
711 |
public_html |
755 |
| Owner | user:<group> recursive (auto-detected) |
Group auto-detection: The script automatically detects the correct group for each user:
- If a group with the same name as the user exists (CWP with PHP-FPM:
user:user), it uses it - If not, falls back to
nobody(CWP with suPHP / cPanel) - This is needed because PHP-FPM on CWP runs with
group = <username>, and usingnobodywould break PHP execution
Non-blocking chown: If chown -R encounters immutable files (e.g., .user.ini with chattr +i), it logs a warning and continues applying all remaining permission steps (chmod 755/644/600/711) instead of aborting. This ensures permissions are corrected even when individual files can't change ownership.
Examples:
# Fix all users
./fix_permissions.sh --all
# Single user only
./fix_permissions.sh -u john
# Simulate without changes
./fix_permissions.sh --dry-run --all
# Multiple users
./fix_permissions.sh -u john -u peter
# Verbose
./fix_permissions.sh --verbose -u johnLightweight reconnaissance script with grep/find. Does not modify files.
What it checks (5 checks):
- Suspicious signatures —
base64_decode,eval(),gzinflate,str_rot13,shell_exec - PHP in uploads —
.phpfiles inside*/uploads/ - Recent files — modified in last 2 days
- Suspicious names —
wp-log,shell,cmd,adminer, etc. - 777 permissions
Examples:
# Quick triage of all /home
./malware_scan.sh
# A specific directory
./malware_scan.sh /home/user/public_html
# Triage first, deep analysis after
./malware_scan.sh /home
python3 malware_scanner.py --scan /home --no-quarantineMoves suspicious PHP files to quarantine and compares WP core with diff.
Warning: This script MOVES files. For report-only, use
malware_scan.shormalware_scanner.py --no-quarantine.
What it does:
- Detects by grep signature → moves to quarantine (except wp-includes/wp-admin)
- PHP in uploads → moves
- Suspicious names → moves
- Validates WP core with
diff -qr - Lists recent
.phpfiles (does not move)
Examples:
# Quarantine suspicious files in /home
./wp_security_scan.sh
# Backup before quarantining (RECOMMENDED)
./wp_backup_db.sh /home && ./wp_security_scan.sh /homeQuarantined files in: /home_D0/fixes/quarantine/
READ-ONLY audit of WordPress reinfection vectors. Detects the entry points and persistence mechanisms that allow malware to return after cleanup.
10 detection categories (52 vectors):
- File persistence — mu-plugins, drop-ins, auto_prepend, index.php @include, double extensions
- Database persistence — WP-Cron hooks, ghost plugins (BD vs disk), injected options
- Server persistence — LD_PRELOAD, .bashrc, at jobs, systemd services/timers
- Config poisoning — .htaccess AddHandler abuse, local php.ini, Nginx auto_prepend, .user.ini
- Upload bypass — uploads/ without PHP execution protection
- Plugins & themes — WP versions, outdated plugins, inactive themes, WP-CRON
- Credentials — wp-config permissions, shared DB credentials, exposed files (.env, .sql, phpinfo)
- PHP hardening — allow_url_include, disable_functions, display_errors, session cookies
- Network — exposed ports (MySQL/Redis/Memcached), xmlrpc.php, DNS mismatch
- Recent files — PHP files modified in last 7 days
Output: logs/reinfection_audit_*.txt + logs/reinfection_audit_*.json
Examples:
# Audit all users
./wp_reinfection_audit.sh
# Audit single user
./wp_reinfection_audit.sh /home/user/public_html
# Recommended: scan + audit together
python3 malware_scanner.py --scan /home --no-quarantine && ./wp_reinfection_audit.shComplementary to malware_scanner.py: The scanner finds malware already present; this audit finds HOW the malware gets in.
READ-ONLY forensic investigation of compromised CWP servers. Analyzes 27 attack vectors to identify the root cause of mass infections (all users infected simultaneously).
Usage: ./cwp_forensic_audit.sh [/home]
Requires: root, bash 4+
27 analysis sections:
| # | Section | What it investigates |
|---|---|---|
| 1 | /home permissions | Account isolation, 777/775 permissions |
| 2 | PHP-FPM pools | Shared vs isolated pool, open_basedir, disable_functions |
| 3 | Apache security | FollowSymLinks, mod_ruid2, open_basedir, suexec |
| 4 | CWP version/config | CWP version, exposed ports, panel accessible |
| 5 | /tmp and PHP sessions | PHP in /tmp, shared sessions, /dev/shm |
| 6 | Cron jobs | Crontabs with wget/curl/base64, propagation vectors |
| 7 | .htaccess | auto_prepend_file, injections, malicious redirects |
| 8 | WordPress status | Outdated versions, vulnerable plugins, xmlrpc, uploads |
| 9 | SSH authentication | PermitRootLogin, passwords, failed attempts |
| 10 | Firewall/ports | firewalld status, unnecessary open ports |
| 11 | Temporal analysis | Patient zero, infection timeline |
| 12 | SUID/SGID binaries | Binaries with anomalous elevated permissions |
| 13 | Suspicious processes | Cryptominers, reverse shells, hidden processes |
| 14 | Network connections | C&C connections, IRC/crypto ports |
| 15 | Hidden files | .ico/.jpg with PHP, hidden files in /home |
| 16 | wp-config.php | Integrity: eval/base64/require /tmp injected |
| 17 | FTP/ProFTPD | Insecure configuration, anonymous access |
| 18 | Apache logs | Brute force, exploit paths, suspicious POSTs |
| 19 | MySQL/MariaDB | Anonymous users, remote root access, test DB |
| 20 | Apache modules | mod_cgi, mod_suphp, dangerous modules |
| 21 | SELinux | Status (enforcing/permissive/disabled) |
| 22 | Kernel/updates | Pending security updates |
| 23 | Cross-account symlinks | Symlinks reading other users' files |
| 24 | authorized_keys | SSH keys injected by attackers |
| 25 | File capabilities | Binaries with elevated capabilities (getcap) |
| 26 | Monitoring | auditd, fail2ban, integrity status |
| 27 | Executive summary | CRITICAL/WARNING/INFO count + verdict |
Reports generated:
| File | Content |
|---|---|
forensic_audit_*.txt |
Human-readable report with categorized findings |
forensic_audit_*.json |
Structured JSON with severity, category, message |
Examples:
# Full audit (READ-ONLY, modifies nothing)
./cwp_forensic_audit.sh
# Specify alternate base directory
./cwp_forensic_audit.sh /home
# Review the report
cat /home_D0/fixes/logs/forensic_audit_*.txt
# Review critical findings in JSON
python3 -c "
import json
data = json.load(open('/home_D0/fixes/logs/forensic_audit_*.json'))
for f in data['findings']:
if f['severity'] == 'CRITICAL':
print(f['category'], '-', f['message'])
"Applies the corrections identified by cwp_forensic_audit.sh. Each phase is independent and supports --dry-run to preview changes.
Usage: ./cwp_harden.sh [options]
Requires: root, bash 4+
Options:
| Option | Description |
|---|---|
--dry-run |
Show what would be done without applying changes |
--fix-perms |
Fix permissions: /home→711, public_html→755, wp-config→600 |
--fix-fpm |
Generate per-user PHP-FPM pools with open_basedir and disable_functions |
--fix-apache |
SymLinksIfOwnerMatch, security headers, ServerTokens Prod |
--fix-ssh |
PermitRootLogin without-password, MaxAuthTries 3, auto-rollback with sshd -t |
--fix-mysql |
Remove anonymous users, revoke remote root access, DROP test DB |
--fix-tmp |
Clean PHP from /tmp and /dev/shm, remount with noexec/nosuid/nodev |
--fix-firewall |
Configure firewalld: only HTTP/HTTPS/SSH(custom)/CWP |
--all |
Apply all corrections |
-h, --help |
Show help |
7 hardening phases:
| Phase | Flag | What it fixes |
|---|---|---|
| 1 | --fix-perms |
/home→711, home/user→711, public_html→755, wp-config.php→600, ownership |
| 2 | --fix-fpm |
Generates per-user PHP-FPM pool with open_basedir, disable_functions, isolated session |
| 3 | --fix-apache |
FollowSymLinks→SymLinksIfOwnerMatch, security headers, ServerTokens, vhosts |
| 4 | --fix-ssh |
PermitRootLogin, MaxAuthTries 3, LoginGraceTime 30, Protocol 2, sshd -t validation |
| 5 | --fix-mysql |
Anonymous users, remote root, test DB, bind-address→127.0.0.1, backup my.cnf |
| 6 | --fix-tmp |
Clean .php from /tmp and /dev/shm, remount noexec/nosuid/nodev, add to fstab |
| 7 | --fix-firewall |
Enable firewalld, allow HTTP/HTTPS/SSH(custom port)/CWP, remove extra services |
Examples:
# ALWAYS start with dry-run
./cwp_harden.sh --dry-run --all
# Fix permissions only
./cwp_harden.sh --fix-perms
# SSH + MySQL hardening
./cwp_harden.sh --fix-ssh --fix-mysql
# PHP-FPM isolation (most important fix for cross-account infection)
./cwp_harden.sh --fix-fpm
# Full hardening (all phases)
./cwp_harden.sh --all
# Recommended flow: audit → dry-run → apply
./cwp_forensic_audit.sh
./cwp_harden.sh --dry-run --all
./cwp_harden.sh --allLog generated: /home_D0/fixes/logs/harden_*.log
READ-ONLY forensic investigation of compromised aaPanel servers. Full adaptation of the CWP script for the aaPanel (BaoTa panel) architecture, with 28 analysis sections covering paths, services, and configurations specific to this panel.
Usage: ./aapanel_forensic_audit.sh [/www/wwwroot]
Requires: root, bash 4+
Key differences from CWP:
| Aspect | CWP | aaPanel |
|---|---|---|
| Sites directory | /home/user/public_html |
/www/wwwroot/sitename/ |
| Web user | Per-user (with FPM pool) | www (shared) |
| Web server | Apache (default) | Nginx (default) |
| Panel path | /usr/local/cwpsrv/ |
/www/server/panel/ |
| PHP config | /opt/alt/phpXX/ |
/www/server/php/XX/ |
| MySQL config | /etc/my.cnf.d/ |
/www/server/mysql/etc/ |
| Web logs | /var/log/httpd/ |
/www/wwwlogs/ |
| Panel DB | PHP-based | SQLite (default.db) |
| Vhost configs | Apache vhosts | /www/server/panel/vhost/nginx/ |
| PHP injection | .htaccess + auto_prepend |
.user.ini + auto_prepend |
28 analysis sections:
| # | Section | What it investigates |
|---|---|---|
| 1 | Directory permissions | Site isolation, 777, wp-config, root ownership |
| 2 | aaPanel version/config | Version, port, security entrance, IP restrict, BasicAuth, API, plugins |
| 3 | Web server security | Nginx (disable_symlinks, vhosts, open_basedir) or Apache (SymLinks, vhosts) |
| 4 | PHP-FPM pools | PHP versions, shared pools, open_basedir, disable_functions |
| 5 | /tmp and sessions | PHP in /tmp, noexec, shared sessions, /dev/shm |
| 6 | Cron jobs | www/root crontabs, aaPanel cron scripts, /etc/cron.d |
| 7 | .user.ini / .htaccess | auto_prepend/append, local php.ini, malicious redirects |
| 8 | WordPress status | Versions, xmlrpc, vulnerable plugins, PHP in uploads |
| 9 | SSH authentication | SSH config, authorized_keys, failed attempts |
| 10 | Firewall/ports | firewalld/ufw/iptables, aaPanel firewall plugin |
| 11 | Temporal analysis | Patient zero, cross-site propagation, identical files |
| 12 | SUID/SGID | Binaries with elevated permissions in /www |
| 13 | Suspicious processes | Cryptominers, reverse shells, standalone PHP |
| 14 | Network connections | C&C connections, suspicious ports |
| 15 | Hidden files | .ico with PHP, hidden directories, dotfiles PHP |
| 16 | wp-config.php | Integrity, eval/base64/require /tmp injections |
| 17 | FTP (Pure-FTPd) | Config, anonymous access, TLS, PHP upload logs |
| 18 | Web access logs | Brute force wp-login, xmlrpc, exploit paths |
| 19 | MySQL/MariaDB | Passwordless users, remote access, bind-address |
| 20 | Web server modules | Nginx autoindex / Apache mod_proxy, mod_info |
| 21 | SELinux / AppArmor | MAC (Mandatory Access Control) status |
| 22 | Kernel/updates | Version, distro, pending updates |
| 23 | Malicious symlinks | Cross-site, to sensitive files, outside wwwroot |
| 24 | authorized_keys | SSH key integrity verification |
| 25 | File capabilities | Binaries with elevated capabilities |
| 26 | Monitoring | auditd, fail2ban, integrity tools |
| 27 | aaPanel-specific | Panel SSL, panel logs, WAF plugin, backups, modified files |
| 28 | Executive summary | CRITICAL/WARNING/INFO count + diagnosis + actions |
Reports generated:
| File | Location |
|---|---|
forensic_audit_*.txt |
/www/backup/forensic_logs/ |
forensic_audit_*.json |
/www/backup/forensic_logs/ |
Examples:
# Full audit (READ-ONLY, modifies nothing)
./aapanel_forensic_audit.sh
# Specify alternate directory
./aapanel_forensic_audit.sh /www/wwwroot
# Review critical findings
grep CRITICAL /www/backup/forensic_logs/forensic_audit_*.txtFull pipeline orchestrator adapted for aaPanel. Obtains MySQL credentials from the panel SQLite DB, handles .user.ini (primary vector on Nginx), and applies www:www permissions.
Usage: ./aapanel_remediate.sh [options] [directory]
Requires: root, bash 4+, Python 3.7+
Options:
| Option | Description |
|---|---|
--clean |
Clean injections with regex |
--disable |
chmod 000 + rename .DISABLED |
--dry-run |
Report only, don't execute actions |
--threshold N |
Minimum score (default: 60) |
--workers N |
Parallel workers (0=sequential) |
--skip-db-backup |
Skip DB backup |
--skip-perms |
Skip permission correction |
6 phases:
| Phase | Description |
|---|---|
| 1 | DB backup (credentials from panel SQLite or fallback wp_backup_db.sh) |
| 2 | Heuristic scan with malware_scanner.py |
| 2b | .user.ini / .htaccess scan + DB (wp_db_scan.sh) |
| 2c | Crontabs (www, root, aaPanel cron scripts) |
| 3 | Classification: MALWARE / LIKELY / SUSPICIOUS |
| 4 | Cleanup: scanner + malicious .user.ini + DB transients |
| 5 | Post-cleanup verification |
| 6 | Permissions: www:www, dirs 755, files 644, wp-config 600 |
Examples:
# Diagnostic only (no changes)
./aapanel_remediate.sh /www/wwwroot
# A specific site
./aapanel_remediate.sh /www/wwwroot/mydomain.com
# Full pipeline with cleanup
./aapanel_remediate.sh --clean /www/wwwroot
# In-place disable (safer, reversible)
./aapanel_remediate.sh --disable /www/wwwroot
# Dry-run with more workers
./aapanel_remediate.sh --dry-run --workers 4 /www/wwwroot
# Recommended flow for aaPanel
./aapanel_forensic_audit.sh # 1. Diagnosis
./aapanel_remediate.sh /www/wwwroot # 2. Scan (report only)
cat /www/backup/forensic_logs/scan_*_files.txt # 3. Review findings
./aapanel_remediate.sh --disable /www/wwwroot # 4. Disable malware
./aapanel_forensic_audit.sh # 5. Re-auditLog generated: /www/backup/forensic_logs/remediate_*.log
Generates executive HTML reports from scanner JSON files. Includes executive summary, detection statistics, detailed findings, action recommendations, and risk matrix by malware type.
Options:
| Option | Description |
|---|---|
JSON_FILE |
Scanner JSON file(s) (positional, required) |
--output DIR |
Output directory (default: logs/) |
--theme THEME |
Visual theme: light, dark (default: light) |
--lang LANG |
Language: es, en (default: es) |
--include-fp |
Include confirmed false positive matrix |
--no-remediation |
Omit remediation section |
--client-name NAME |
Client name for the header |
--scan-date DATE |
Scan date (default: taken from JSON) |
Examples:
# Generate HTML report from a scan
python3 generate_html_report.py logs/scan_2026-03-24_03-41-04.json
# All scans, dark theme
python3 generate_html_report.py logs/scan_*.json --theme dark
# Report for client, in English
python3 generate_html_report.py logs/scan_*.json --client-name "Acme Corp" --lang en
# Without remediation section (findings only)
python3 generate_html_report.py logs/scan_*.json --no-remediation
# Output to specific directory
python3 generate_html_report.py logs/scan_*.json --output /tmp/reports/Report generated: logs/report_scan_<timestamp>.html
Monitors a directory with inotify (Linux kernel-level file events) and runs malware_scanner.py on every PHP/JS/.htaccess file that is created, modified, or renamed — the same mechanism commercial antivirus products use. No polling, near-zero CPU at rest.
Requirements:
pip3 install watchdogOptions:
| Option | Description |
|---|---|
--watch DIR |
Directory to monitor recursively (default: /home) |
--scanner-path PATH |
Path to malware_scanner.py (default: same directory) |
--log-dir DIR |
Directory for per-scan JSON logs (default: ./logs) |
--threshold N |
Minimum score to report a finding (default: 60) |
--debounce SECS |
Wait N seconds after last event before scanning (default: 3) |
--max-file-mb N |
Skip files larger than N MB (default: 10) |
--max-per-min N |
Maximum scans per minute — rate limiter (default: 120) |
--alert-webhook URL |
POST JSON alert to this URL for each MALWARE verdict |
--log-file FILE |
Also write watcher log to this file |
-v, --verbose |
Show inotify events and scanner debug output |
Detected events:
| Event | Detected? | Mechanism |
|---|---|---|
| New PHP file uploaded (FTP/SFTP) | ✅ | inotify IN_CLOSE_WRITE |
| Modified PHP file (injection) | ✅ | inotify IN_MODIFY |
| Rename .tmp → .php (dropper) | ✅ | inotify IN_MOVED_TO |
| PHP runtime execution | Covered if the file was written first | |
| Binary/shell execution | ❌ | Requires fanotify (more complex) |
Installation as systemd service:
# 1. Install dependency
pip3 install watchdog
# 2. Copy files to the server
scp malware_watcher.py malware_watcher.service root@server:/opt/malscanner/
# 3. Edit paths in the service file
nano /opt/malscanner/malware_watcher.service
# → Adjust: WorkingDirectory, ExecStart --scanner-path, --log-dir
# 4. Install and start the service
cp /opt/malscanner/malware_watcher.service /etc/systemd/system/
systemctl daemon-reload
systemctl enable --now malware-watcher
# 5. Check live status
systemctl status malware-watcher
journalctl -u malware-watcher -fManual test (before installing as service):
# Basic test — verbose, watch /home
python3 malware_watcher.py --watch /home --verbose
# With alert webhook (Slack, Discord, custom API)
python3 malware_watcher.py --watch /home --alert-webhook https://hooks.example.com/alerts
# With log to file + higher threshold
python3 malware_watcher.py --watch /home --threshold 75 --log-file /var/log/malwatch.log
# Lower rate limit (conserve CPU on high-traffic servers)
python3 malware_watcher.py --watch /home --max-per-min 30 --debounce 5Service management:
# View live alerts
journalctl -u malware-watcher -f
# Stop service
systemctl stop malware-watcher
# Restart after updating malware_scanner.py or signatures/
systemctl restart malware-watcher
# Disable on server shutdown (leave installed)
systemctl disable malware-watcherConfiguration file (malware_watcher.service):
[Service]
ExecStart=/usr/bin/python3 /opt/malscanner/malware_watcher.py \
--watch /home \
--scanner-path /opt/malscanner/malware_scanner.py \
--log-dir /opt/malscanner/logs \
--threshold 60 \
--debounce 3 \
--max-file-mb 10 \
--max-per-min 120 \
--log-file /var/log/malware_watcher.log
CPUQuota=40%
PrivateTmp=true
NoNewPrivileges=trueAutomatic installer for malware_watcher.py as a systemd service. Handles dependency installation, file deployment, and service configuration.
Modes:
| Mode | Description |
|---|---|
./install_watcher.sh |
Interactive install — prompts for each setting |
./install_watcher.sh --install |
Non-interactive install with defaults |
./install_watcher.sh --uninstall |
Remove service and optionally delete files |
./install_watcher.sh --update |
Update toolkit files, preserve configuration |
./install_watcher.sh --status |
Show service state and installation info |
Defaults (overridable in interactive mode):
| Setting | Default |
|---|---|
| Install directory | /opt/malscanner |
| Watch directory | /home |
| Score threshold | 60 |
| Debounce (seconds) | 3 |
| Max file size (MB) | 10 |
| Rate limit (scans/min) | 120 |
| CPU quota | 40% |
Usage examples:
# Interactive install (recommended first time)
sudo ./install_watcher.sh
# Non-interactive with all defaults
sudo ./install_watcher.sh --install
# Update after pulling new scanner signatures
sudo ./install_watcher.sh --update
# Check service health
./install_watcher.sh --status
# Full removal
sudo ./install_watcher.sh --uninstall# 1. Full diagnostic (no changes)
./remediate.sh /home
# 2. Review reports in /home_D0/fixes/logs/
# 3. Execute cleanup
./remediate.sh --clean /home# 1. Backup databases
./wp_backup_db.sh /home
# 2. Heuristic scan (report only)
python3 malware_scanner.py --scan /home --no-quarantine
# 3. Database scan
./wp_db_scan.sh /home
# 4. Check crontabs (clean first = prevent reinfection)
./cron_check.sh --fix
# 5. Clean malware + verify
python3 malware_scanner.py --scan /home --clean --verify
# 6. Clean malicious DB transients
./wp_db_scan.sh --fix /home
# 7. Restore WP core
./wp_reemplaza.sh
# 8. Re-verify
python3 malware_scanner.py --scan /home --no-quarantine
# 9. Fix permissions
./fix_permissions.sh --allWhen all users on a CWP server were infected simultaneously.
# 1. Upload scripts to server from Windows
scp -P 2222 cwp_forensic_audit.sh cwp_harden.sh root@SERVER:/root/
scp -P 2222 malware_scanner.py remediate.sh root@SERVER:/root/
scp -P 2222 wp_backup_db.sh wp_db_scan.sh cron_check.sh root@SERVER:/root/
scp -P 2222 fix_permissions.sh wp_reemplaza.sh root@SERVER:/root/
scp -rP 2222 signatures/ root@SERVER:/root/signatures/
# 2. On the Linux server, set execute permissions
chmod +x /root/*.sh
# 3. Create working directories
mkdir -p /home_D0/fixes/{logs,quarantine,backups,wordpress}
# 4. Run forensic audit (READ-ONLY, modifies nothing)
./cwp_forensic_audit.sh
# 5. Review the report — identify root cause
cat /home_D0/fixes/logs/forensic_audit_*.txt
# 6. Backup databases
./wp_backup_db.sh /home
# 7. Heuristic malware scan (report only)
python3 malware_scanner.py --scan /home --no-quarantine --report both
# 8. Review findings and false positives
cat /home_D0/fixes/logs/scan_*_files.txt
# 9. Apply hardening with dry-run first
./cwp_harden.sh --dry-run --all
# 10. Apply real hardening (phase by phase recommended)
./cwp_harden.sh --fix-perms
./cwp_harden.sh --fix-fpm
./cwp_harden.sh --fix-apache
./cwp_harden.sh --fix-ssh
./cwp_harden.sh --fix-mysql
./cwp_harden.sh --fix-tmp
./cwp_harden.sh --fix-firewall
# 11. Clean malware and verify
./cron_check.sh --fix
python3 malware_scanner.py --scan /home --clean --verify --report both
# 12. Restore WordPress core + permissions
./wp_reemplaza.sh
./fix_permissions.sh --all
# 13. Generate executive HTML report
python3 generate_html_report.py /home_D0/fixes/logs/scan_*.json --client-name "My Server"# Upload entire toolkit to server
scp -P 2222 -r . root@SERVER:/root/malware_toolkit/
# Or upload only essential scripts
scp -P 2222 malware_scanner.py remediate.sh cwp_forensic_audit.sh cwp_harden.sh \
wp_backup_db.sh wp_db_scan.sh cron_check.sh fix_permissions.sh \
wp_reemplaza.sh generate_html_report.py root@SERVER:/root/
scp -rP 2222 signatures/ root@SERVER:/root/signatures/# Set execute permissions
chmod +x /root/*.sh
# Create working directories
mkdir -p /home_D0/fixes/{logs,quarantine,backups,wordpress}
# Verify Python dependencies
python3 -c "import json, sys, re, hashlib; print('OK')"
# Verify system tools
for cmd in mysqldump mysql gzip find rsync stat chown chmod; do
command -v "$cmd" &>/dev/null && echo "✓ $cmd" || echo "✗ $cmd MISSING"
done| Layer | File | Signatures |
|---|---|---|
| 1. Core | Embedded in malware_scanner.py |
~70 |
| 2. Internal | signatures/internal_knowledge.json |
6 |
| 3. Community | signatures/community.json |
6 |
| 4. Local IOC | signatures/local_iocs.json |
9 |
| 5. YARA | signatures/yara/php_webshells_curated.yar |
5 |
| 6. Fallback | signatures/yara_fallback.json |
5 |
If yara-python is not installed, regex fallback activates automatically.
To add detections:
- New IOCs →
signatures/local_iocs.json - Internal signatures →
signatures/internal_knowledge.json - YARA rules →
signatures/yara/php_webshells_curated.yar - Fallback →
signatures/yara_fallback.json
- Python 3.7+
- Linux/CentOS (default paths point to
/home_D0/fixes) - Bash 4+ (CentOS 7+, Ubuntu 16.04+)
- Root for most scripts
- System tools:
mysqldump,mysql,gzip,find,flock,rsync,stat,chown,chmod
Optional dependency:
yara-pythonfor real YARA rule compilation
Base paths are defined in the main scripts:
| Variable | Default | Used in | Description |
|---|---|---|---|
QUARANTINE_DIR |
/home_D0/fixes/quarantine |
malware_scanner.py |
Quarantined files |
LOG_DIR |
/home_D0/fixes/logs |
All | Logs and reports |
WP_CLEAN_DIR |
/home_D0/fixes/wordpress |
malware_scanner.py |
Clean WP copy |
SIGNATURES_DIR |
./signatures |
malware_scanner.py |
External signatures |
HOME_BASE |
/home |
cwp_forensic_audit.sh, cwp_harden.sh |
User base directory |
Adjust these paths before running in production.
malware_scanner.py # Main heuristic engine (6 signature layers)
remediate.sh # 7-phase pipeline orchestrator
cwp_forensic_audit.sh # CWP forensic audit (27 sections)
cwp_harden.sh # Post-compromise CWP hardening (7 phases)
aapanel_forensic_audit.sh # aaPanel forensic audit (28 sections)
aapanel_remediate.sh # aaPanel remediation pipeline (6 phases)
generate_html_report.py # Executive HTML report generator
wp_backup_db.sh # WordPress database backup
wp_db_scan.sh # Database scanning (injections)
cron_check.sh # Malicious crontab detection
wp_reemplaza.sh # WordPress core restoration
fix_permissions.sh # Hosting permission correction
malware_scan.sh # Quick triage (read-only)
wp_security_scan.sh # Basic quarantine by signatures
wp_reinfection_audit.sh # Reinfection vector audit (52 checks, 10 categories)
malware_watcher.py # Real-time resident daemon (inotify antivirus)
malware_watcher.service # systemd unit for malware_watcher
install_watcher.sh # Installer for malware_watcher service
signatures/
├── internal_knowledge.json # 6 internal signatures
├── community.json # 6 community signatures
├── local_iocs.json # 9 local IOCs (extensible)
├── yara/ # 5 YARA rules (optional)
└── yara_fallback.json # 5 regex fallback rules
malware_refs/ # Reference malware samples for testing
logs/ # Scan reports (JSON, TXT, HTML, rm_*.txt)
SKILL.md # Complete operational guide
- DON'T run
--cleanon the first pass without reviewing findings - DON'T delete files without a prior DB backup
- DON'T blindly trust
scan_*.rm_ALL.txt— review false positives - DON'T ignore SUSPICIOUS findings
- DON'T forget to clean crontabs before cleaning files (causes reinfection)
- DON'T leave 777 permissions after remediation
- DON'T restore WP core without verifying the correct version
This project is licensed under Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International (CC BY-NC-ND 4.0).
You may share the material with attribution, but not for commercial use and without derivative works.
See the LICENSE file for the full text.
This project is provided for incident analysis and response tasks. Use is at the user's own risk.
No se garantiza la recuperación total de sitios WordPress comprometidos. En incidentes severos (persistencia profunda, backdoors múltiples, corrupción de BD o reinfección activa), puede no ser posible restaurar completamente el sitio.
Recomendaciones:
- Mantener respaldos íntegros y verificados (archivos + base de datos)
- Probar primero en entornos de staging
- Validar manualmente cambios críticos antes de aplicar en producción