#!/bin/bash ################################################################################ # Malware Scanner ################################################################################ # Purpose: Comprehensive malware scanning with multiple engines # Supports: ImunifyAV, ClamAV, Maldet (LMD) # Scan scope: Single domain, user account, or entire server ################################################################################ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" source "$SCRIPT_DIR/lib/common-functions.sh" 2>/dev/null || true source "$SCRIPT_DIR/lib/system-detect.sh" 2>/dev/null || true source "$SCRIPT_DIR/lib/user-manager.sh" 2>/dev/null || true source "$SCRIPT_DIR/lib/reference-db.sh" 2>/dev/null || true source "$SCRIPT_DIR/lib/ip-reputation.sh" 2>/dev/null || true # Arrays for docroots and scanners declare -a docroot_array declare -a sanitized_docroot declare -a remove_docroot declare -a available_scanners # Individual scanner detection functions is_imunify_installed() { command -v imunify-antivirus &>/dev/null || [ -f "/usr/bin/imunify-antivirus" ] } is_clamav_installed() { command -v clamscan &>/dev/null || \ [ -f "/usr/local/cpanel/3rdparty/bin/clamscan" ] || \ rpm -qa | grep -q "cpanel-clamav" } is_maldet_installed() { command -v maldet &>/dev/null || [ -f "/usr/local/sbin/maldet" ] } is_rkhunter_installed() { command -v rkhunter &>/dev/null || [ -f "/usr/bin/rkhunter" ] } # Scanner detection detect_scanners() { available_scanners=() if is_imunify_installed; then available_scanners+=("imunify") fi if is_clamav_installed; then available_scanners+=("clamav") fi if is_maldet_installed; then available_scanners+=("maldet") fi if is_rkhunter_installed; then available_scanners+=("rkhunter") fi if [ ${#available_scanners[@]} -eq 0 ]; then echo -e "${RED}No malware scanners detected!${NC}" echo "" show_scanner_installation_guide return 1 fi return 0 } # Show installation instructions for missing scanners show_scanner_installation_guide() { echo -e "${YELLOW}Available Malware Scanners:${NC}" echo "" # Check ImunifyAV if ! is_imunify_installed; then echo -e "${CYAN}ImunifyAV${NC} - FREE real-time malware scanner" echo " Status: Not installed" echo " Installation (cPanel):" echo " yum install imunify-antivirus imunify-antivirus-cpanel" echo " /opt/alt/python35/share/imunify360/scripts/av-userside-plugin.sh" echo " Installation (script method):" echo " wget https://repo.imunify360.cloudlinux.com/defence360/imav-deploy.sh" echo " bash imav-deploy.sh" echo " Note: ImunifyAV is FREE. Imunify360 is the paid version." echo " Docs: https://docs.imunify360.com/imunifyav/" echo "" else echo -e "${GREEN}✓ ImunifyAV${NC} - Installed (FREE version)" echo "" fi # Check ClamAV if ! is_clamav_installed; then echo -e "${CYAN}ClamAV${NC} - Open source antivirus engine" echo " Status: Not installed" echo " Installation (cPanel):" echo " /scripts/update_local_rpm_versions --edit target_settings.clamav installed" echo " /scripts/check_cpanel_rpms --fix --targets=clamav" echo " Installation (manual):" echo " yum install clamav clamav-update # RHEL/CentOS" echo " apt-get install clamav clamav-daemon # Debian/Ubuntu" echo " freshclam # Update virus definitions" echo "" else echo -e "${GREEN}✓ ClamAV${NC} - Installed" echo "" fi # Check Maldet if ! is_maldet_installed; then echo -e "${CYAN}Maldet (LMD)${NC} - Linux Malware Detect" echo " Status: Not installed" echo " Installation:" echo " cd /tmp" echo " wget http://www.rfxn.com/downloads/maldetect-current.tar.gz" echo " tar -xzf maldetect-current.tar.gz" echo " cd maldetect-*" echo " ./install.sh" echo " Docs: https://www.rfxn.com/projects/linux-malware-detect/" echo "" else echo -e "${GREEN}✓ Maldet${NC} - Installed" echo "" fi # Check Rootkit Hunter if ! is_rkhunter_installed; then echo -e "${CYAN}Rootkit Hunter${NC} - Rootkit/backdoor/exploit scanner" echo " Status: Not installed" echo " Installation:" echo " yum install epel-release -y # Enable EPEL repo" echo " yum install rkhunter -y" echo " rkhunter --update # Update definitions" echo " rkhunter --propupd # Initialize baseline" echo " Docs: https://rkhunter.sourceforge.net/" echo "" else echo -e "${GREEN}✓ Rootkit Hunter${NC} - Installed" echo "" fi echo -e "${YELLOW}Recommendation:${NC} Install at least ClamAV + RKHunter (both free) for comprehensive protection" echo "" } # Install all scanners at once install_all_scanners() { echo "" print_header "Install All Malware Scanners" echo "This will install:" echo " • ClamAV (free, open source)" echo " • Maldet (free, Linux-specific)" echo " • ImunifyAV (FREE version)" echo " • Rootkit Hunter (free, rootkit detection)" echo "" echo -e "${YELLOW}Note: ImunifyAV is FREE. Imunify360 is the paid version.${NC}" echo "" read -p "Proceed with installation? (yes/no): " confirm if [ "$confirm" != "yes" ]; then echo "Cancelled." read -p "Press Enter to continue..." return 0 fi echo "" echo "==========================================" echo "Installing Scanners" echo "==========================================" echo "" # Install ClamAV if ! is_clamav_installed; then echo -e "${CYAN}[1/4] Installing ClamAV...${NC}" if [ -f "/usr/local/cpanel/cpanel" ]; then # cPanel method - check if already installed but not configured if rpm -qa | grep -q "cpanel-clamav"; then echo -e "${GREEN}✓ ClamAV already installed (cPanel)${NC}" else /scripts/update_local_rpm_versions --edit target_settings.clamav installed 2>/dev/null /scripts/check_cpanel_rpms --fix --targets=clamav 2>&1 | grep -E "Installing|Updating|up to date" fi elif command -v yum &>/dev/null; then yum install -y clamav clamav-update 2>&1 | grep -E "Installing|Updating|already installed" elif command -v apt-get &>/dev/null; then apt-get update && apt-get install -y clamav clamav-daemon fi if is_clamav_installed; then echo -e "${GREEN}✓ ClamAV installed${NC}" # Find freshclam binary local freshclam_bin=$(command -v freshclam || find /usr -name freshclam 2>/dev/null | head -1) # Update virus signatures immediately if [ -n "$freshclam_bin" ]; then echo " → Updating virus signatures (this may take a moment)..." $freshclam_bin 2>&1 | grep -E "updated|Downloaded|up-to-date" || $freshclam_bin &>/dev/null echo -e " ${GREEN}✓${NC} Signatures updated" fi else echo -e "${RED}✗ ClamAV installation failed${NC}" fi else echo -e "${GREEN}✓ ClamAV already installed${NC}" fi echo "" # Install Maldet if ! is_maldet_installed; then echo -e "${CYAN}[2/4] Installing Maldet...${NC}" cd /tmp wget -q http://www.rfxn.com/downloads/maldetect-current.tar.gz if [ -f maldetect-current.tar.gz ]; then tar -xzf maldetect-current.tar.gz cd maldetect-* 2>/dev/null ./install.sh &>/dev/null cd /tmp rm -rf maldetect-* fi if is_maldet_installed; then echo -e "${GREEN}✓ Maldet installed${NC}" # Update malware signatures immediately echo " → Updating malware signatures..." maldet -u 2>&1 | grep -E "update completed|signatures" || maldet -u &>/dev/null echo -e " ${GREEN}✓${NC} Signatures updated" else echo -e "${RED}✗ Maldet installation failed${NC}" fi else echo -e "${GREEN}✓ Maldet already installed${NC}" fi echo "" # Install ImunifyAV (FREE version) if ! is_imunify_installed; then echo -e "${CYAN}[3/4] Installing ImunifyAV (FREE)...${NC}" echo " This may take several minutes - please wait..." # Use deployment script method (most reliable) cd /tmp if [ -f "imav-deploy.sh" ]; then rm -f imav-deploy.sh fi wget -q https://repo.imunify360.cloudlinux.com/defence360/imav-deploy.sh if [ -f imav-deploy.sh ]; then # Run deployment script with progress indicators bash imav-deploy.sh 2>&1 | grep -E "Installing|Installed|Complete|Error|Failed" || bash imav-deploy.sh rm -f imav-deploy.sh # Enable cPanel UI plugin if installed if [ -f "/opt/alt/python35/share/imunify360/scripts/av-userside-plugin.sh" ]; then echo " → Enabling cPanel UI plugin..." /opt/alt/python35/share/imunify360/scripts/av-userside-plugin.sh &>/dev/null fi else echo -e "${RED} Failed to download installation script${NC}" fi if is_imunify_installed; then echo -e "${GREEN}✓ ImunifyAV (FREE) installed${NC}" echo " No license key required - this is the FREE version" # Find imunify-antivirus binary local imunify_bin=$(command -v imunify-antivirus || find /usr -name imunify-antivirus 2>/dev/null | head -1) # Update malware signatures immediately if [ -n "$imunify_bin" ]; then echo " → Updating malware signatures..." $imunify_bin update 2>&1 | grep -E "updated|Success|completed" || $imunify_bin update &>/dev/null echo -e " ${GREEN}✓${NC} Signatures updated" fi else echo -e "${RED}✗ ImunifyAV installation failed${NC}" fi else echo -e "${GREEN}✓ ImunifyAV already installed${NC}" fi echo "" # Install Rootkit Hunter if ! is_rkhunter_installed; then echo -e "${CYAN}[4/4] Installing Rootkit Hunter...${NC}" # Ensure EPEL repo is enabled if command -v yum &>/dev/null; then if ! rpm -qa | grep -q epel-release; then echo " → Installing EPEL repository..." yum install -y epel-release 2>&1 | grep -E "Installing|Installed|already installed" fi # Install rkhunter yum install -y rkhunter 2>&1 | grep -E "Installing|Installed|already installed" elif command -v apt-get &>/dev/null; then apt-get update && apt-get install -y rkhunter fi if is_rkhunter_installed; then echo -e "${GREEN}✓ Rootkit Hunter installed${NC}" # Update definitions echo " → Updating rootkit definitions..." rkhunter --update 2>&1 | grep -E "updated|downloaded" || rkhunter --update &>/dev/null echo -e " ${GREEN}✓${NC} Definitions updated" # Initialize baseline (propupd creates file property database) echo " → Initializing baseline database..." rkhunter --propupd &>/dev/null echo -e " ${GREEN}✓${NC} Baseline initialized" else echo -e "${RED}✗ Rootkit Hunter installation failed${NC}" fi else echo -e "${GREEN}✓ Rootkit Hunter already installed${NC}" fi echo "" echo "==========================================" echo "Installation Complete" echo "==========================================" echo "" # Re-detect scanners detect_scanners echo "" read -p "Press Enter to continue..." } # Detect control panel and gather docroots detect_control_panel() { docroot_array=() # Use system-detect.sh if available, otherwise detect if [ -n "$SYS_CONTROL_PANEL" ]; then CONTROL_PANEL="$SYS_CONTROL_PANEL" elif [ -f "/etc/userdatadomains" ]; then CONTROL_PANEL="cpanel" elif [ -f "/usr/local/psa/version" ]; then CONTROL_PANEL="plesk" elif [ -d "/usr/local/interworx/" ]; then CONTROL_PANEL="interworx" else CONTROL_PANEL="none" fi # cPanel-specific setup if [ "$CONTROL_PANEL" = "cpanel" ]; then # Add cPanel 3rdparty bin to PATH only for cPanel export PATH=/usr/local/cpanel/3rdparty/bin/:$PATH while IFS= read -r line; do # Format: domain: user==owner==main==domain==docroot==... # Extract docroot (field 5, 0-indexed field 4) docroot=$(echo "$line" | awk -F'==' '{print $5}') [ -n "$docroot" ] && [ -d "$docroot" ] && docroot_array+=("$docroot") done < <(cut -d: -f2- /etc/userdatadomains | sort -u) # Plesk-specific elif [ "$CONTROL_PANEL" = "plesk" ]; then while IFS= read -r domain; do docroot=$(plesk bin site -i "$domain" 2>/dev/null | grep "WWW-Root" | awk '{print $2}') [ -n "$docroot" ] && docroot_array+=("$docroot") done < <(plesk bin site --list 2>/dev/null) # InterWorx-specific (improved with proper path structure) elif [ "$CONTROL_PANEL" = "interworx" ]; then # InterWorx structure: /home/username/domain.com/html # Find all html directories in the InterWorx structure while IFS= read -r docroot; do [ -n "$docroot" ] && [ -d "$docroot" ] && docroot_array+=("$docroot") done < <(find /home/*/*/html -maxdepth 0 -type d 2>/dev/null | sort -u) else CONTROL_PANEL="none" echo -e "${YELLOW}No control panel detected${NC}" echo "Manual path selection required" return 1 fi # Remove subdirectory docroots (avoid scanning same files twice) sanitize_docroots return 0 } # Remove subdirectory docroots from array sanitize_docroots() { remove_docroot=() for search_value in "${docroot_array[@]}"; do # Count how many paths contain this value count=$(printf '%s\n' "${docroot_array[@]}" | grep -c "$search_value" || true) if [ "$count" -gt 1 ]; then # Find subdirectories and mark for removal while IFS= read -r subdir; do if [ "$subdir" != "$search_value" ]; then remove_docroot+=("$subdir") fi done < <(printf '%s\n' "${docroot_array[@]}" | grep "$search_value") fi done # Build sanitized array sanitized_docroot=() for docroot in "${docroot_array[@]}"; do # Check if this docroot is in remove list skip=0 for remove in "${remove_docroot[@]}"; do if [ "$docroot" = "$remove" ]; then skip=1 break fi done if [ "${skip:-0}" -eq 0 ]; then sanitized_docroot+=("$docroot") fi done } # Get docroots for specific user get_user_docroots() { local username="$1" local user_docroots=() if [ "$CONTROL_PANEL" = "cpanel" ]; then while IFS= read -r line; do docroot=$(echo "$line" | awk -F'==' '{print $5}') [ -n "$docroot" ] && [ -d "$docroot" ] && user_docroots+=("$docroot") done < <(grep ":.*${username}==" /etc/userdatadomains | cut -d: -f2- | sort -u) elif [ "$CONTROL_PANEL" = "interworx" ]; then # Use user-manager.sh to get all domains for this user local domains=$(get_user_domains "$username") if [ -n "$domains" ]; then while IFS= read -r domain; do # InterWorx: /home/username/domain.com/html local docroot="/home/${username}/${domain}/html" [ -d "$docroot" ] && user_docroots+=("$docroot") done <<< "$domains" fi else echo -e "${RED}User-specific scanning only supported on cPanel/InterWorx${NC}" return 1 fi echo "${user_docroots[@]}" } # Get docroot for specific domain get_domain_docroot() { local domain="$1" local domain_docroot="" if [ "$CONTROL_PANEL" = "cpanel" ]; then domain_docroot=$(grep "^${domain}:" /etc/userdatadomains | cut -d= -f5 | sed 's/==/=/g') elif [ "$CONTROL_PANEL" = "plesk" ]; then domain_docroot=$(plesk bin site -i "$domain" 2>/dev/null | grep "WWW-Root" | awk '{print $2}') elif [ "$CONTROL_PANEL" = "interworx" ]; then # Find which user owns this domain using vhost configs local username=$(grep -l "ServerName ${domain}" /etc/httpd/conf.d/vhost_*.conf 2>/dev/null | head -1 | \ xargs grep "SuexecUserGroup" 2>/dev/null | awk '{print $2}') if [ -n "$username" ]; then # InterWorx: /home/username/domain.com/html domain_docroot="/home/${username}/${domain}/html" fi else echo -e "${RED}Domain lookup only supported on cPanel/Plesk/InterWorx${NC}" return 1 fi echo "$domain_docroot" } # Memory check before scanning check_memory() { local total_mem=$(free -m | awk '/^Mem:/{print $2}') local avail_mem=$(free -m | awk '/^Mem:/{print $7}') local min_total=2048 # 2GB local min_avail=512 # 512MB if [ "$total_mem" -lt "$min_total" ] || [ "$avail_mem" -lt "$min_avail" ]; then echo -e "${YELLOW}WARNING: Low memory detected${NC}" echo "Total: ${total_mem}MB | Available: ${avail_mem}MB" echo "" echo "Running a full scan may cause high load or OOM conditions." echo "" read -p "Continue anyway? (yes/no): " confirm if [ "$confirm" != "yes" ]; then echo "Scan cancelled" return 1 fi fi return 0 } # ImunifyAV scanner # Generate standalone malware scan script generate_standalone_scanner() { local scan_paths=("$@") if [ ${#scan_paths[@]} -eq 0 ]; then echo -e "${RED}No paths to scan${NC}" return 1 fi # Create session ID and directory local session_id="malware-$(date +%Y%m%d-%H%M%S)" local session_dir="/opt/${session_id}" echo "" print_header "Generating Standalone Scanner" echo "Session ID: $session_id" echo "Location: $session_dir" echo "" # Create directory structure mkdir -p "$session_dir"/{logs,results} # Create standalone scan script cat > "$session_dir/scan.sh" << 'STANDALONE_EOF' #!/bin/bash ################################################################################ # Standalone Malware Scanner ################################################################################ # Auto-generated by Server Management Toolkit # This script is self-contained and can run independently ################################################################################ # Colors RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' CYAN='\033[0;36m' NC='\033[0m' # Get script directory SCAN_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" LOG_DIR="$SCAN_DIR/logs" RESULTS_DIR="$SCAN_DIR/results" # Session info SESSION_LOG="$LOG_DIR/session.log" SUMMARY_FILE="$RESULTS_DIR/summary.txt" INFECTED_LIST="$RESULTS_DIR/infected_files.txt" # Logging function log_message() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "$SESSION_LOG" } # Activity spinner for long-running scans show_spinner() { local pid=$1 local message=$2 local spin='⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏' local i=0 while kill -0 $pid 2>/dev/null; do i=$(( (i+1) % 10 )) printf "\r ⏳ $message ${spin:$i:1} " sleep 0.2 done printf "\r ✓ $message - Complete\n" } # Format elapsed time format_time() { local seconds=$1 if [ $seconds -lt 60 ]; then echo "${seconds}s" elif [ $seconds -lt 3600 ]; then printf "%dm %ds" $((seconds / 60)) $((seconds % 60)) else printf "%dh %dm" $((seconds / 3600)) $(((seconds % 3600) / 60)) fi } # Cleanup function for trap handler cleanup_on_exit() { local exit_code=$? echo "" # Remove running marker file rm -f "$SCAN_DIR/.scan_running" # Only log if session log exists if [ -f "$SESSION_LOG" ]; then log_message "Cleanup triggered (exit code: $exit_code)" fi # Remove temporarily installed RKHunter if [ "${RKHUNTER_TEMP_INSTALLED:-false}" = "true" ]; then if [ -f "$SESSION_LOG" ]; then log_message "Removing temporarily installed RKHunter..." fi echo "→ Cleaning up: Removing Rootkit Hunter..." if command -v yum &>/dev/null; then yum remove -y rkhunter &>/dev/null 2>&1 if [ -f "$SESSION_LOG" ]; then log_message "RKHunter removed" fi fi fi # Save interrupted status (only if summary file directory exists) if [ $exit_code -ne 0 ] && [ -d "$RESULTS_DIR" ]; then { echo "" echo "SCAN INTERRUPTED" echo "Exit code: $exit_code" echo "Time: $(date)" } >> "$SUMMARY_FILE" if [ -f "$SESSION_LOG" ]; then log_message "Scan interrupted with exit code: $exit_code" fi fi } # Set trap for cleanup on exit, interrupt, or termination trap cleanup_on_exit EXIT INT TERM # Banner clear echo "========================================" echo "Standalone Malware Scanner" echo "========================================" echo "Session: $(basename $SCAN_DIR)" echo "Started: $(date)" echo "========================================" echo "" log_message "Scan session started" # Create marker file to indicate scan is running touch "$SCAN_DIR/.scan_running" # Detect available scanners AVAILABLE_SCANNERS=() if command -v imunify-antivirus &>/dev/null; then AVAILABLE_SCANNERS+=("imunify") log_message "Detected: ImunifyAV" fi if command -v clamscan &>/dev/null; then AVAILABLE_SCANNERS+=("clamav") log_message "Detected: ClamAV" fi if command -v maldet &>/dev/null; then AVAILABLE_SCANNERS+=("maldet") log_message "Detected: Maldet" fi # Track if rkhunter was auto-installed (for cleanup) RKHUNTER_TEMP_INSTALLED=false if command -v rkhunter &>/dev/null; then AVAILABLE_SCANNERS+=("rkhunter") log_message "Detected: Rootkit Hunter" else # Auto-install rkhunter temporarily for this scan log_message "RKHunter not found - installing temporarily..." echo "→ Installing Rootkit Hunter (temporary, will be removed after scan)..." if command -v yum &>/dev/null; then # Ensure EPEL is available if ! rpm -qa | grep -q epel-release; then yum install -y epel-release &>/dev/null fi # Install rkhunter yum install -y rkhunter &>/dev/null if command -v rkhunter &>/dev/null; then # Update definitions and initialize baseline rkhunter --update &>/dev/null rkhunter --propupd &>/dev/null AVAILABLE_SCANNERS+=("rkhunter") RKHUNTER_TEMP_INSTALLED=true log_message "RKHunter installed temporarily" echo " ✓ RKHunter installed (will be removed after scan)" fi fi fi if [ ${#AVAILABLE_SCANNERS[@]} -eq 0 ]; then log_message "ERROR: No scanners found!" echo -e "${RED}No malware scanners detected!${NC}" exit 1 fi log_message "Found ${#AVAILABLE_SCANNERS[@]} scanner(s): ${AVAILABLE_SCANNERS[*]}" # Scan paths (will be replaced) SCAN_PATHS=() PLACEHOLDER_SCAN_PATHS # Validate scan paths log_message "Validating scan paths..." VALID_PATHS=() for path in "${SCAN_PATHS[@]}"; do if [ -e "$path" ]; then if [ -r "$path" ]; then VALID_PATHS+=("$path") log_message "✓ Valid path: $path" else log_message "WARNING: Path not readable: $path (skipping)" echo "⚠️ WARNING: Cannot read $path (permission denied) - skipping" fi else log_message "WARNING: Path does not exist: $path (skipping)" echo "⚠️ WARNING: Path does not exist: $path - skipping" fi done if [ ${#VALID_PATHS[@]} -eq 0 ]; then log_message "ERROR: No valid paths to scan!" echo -e "${RED}ERROR: No valid paths to scan!${NC}" exit 1 fi # Use only valid paths SCAN_PATHS=("${VALID_PATHS[@]}") log_message "Scanning ${#SCAN_PATHS[@]} valid path(s)" # Check available disk space for logs log_message "Checking disk space..." SCAN_DIR_FS=$(df -P "$SCAN_DIR" | tail -1 | awk '{print $6}') AVAILABLE_KB=$(df -P "$SCAN_DIR" | tail -1 | awk '{print $4}') AVAILABLE_MB=$((AVAILABLE_KB / 1024)) if [ "$AVAILABLE_MB" -lt 100 ]; then log_message "WARNING: Low disk space ($AVAILABLE_MB MB available)" echo "⚠️ WARNING: Low disk space on $SCAN_DIR_FS ($AVAILABLE_MB MB available)" echo "Scan logs may be large. Recommend at least 100 MB free space." echo "" read -t 10 -p "Continue anyway? (y/N): " continue_scan if [[ ! "$continue_scan" =~ ^[Yy]$ ]]; then log_message "Scan cancelled due to low disk space" echo "Scan cancelled." exit 0 fi else log_message "Disk space OK: $AVAILABLE_MB MB available" fi # Initialize summary { echo "==========================================" echo "Malware Scan Summary Report" echo "==========================================" echo "Session: $(basename $SCAN_DIR)" echo "Started: $(date)" echo "Scanners: ${AVAILABLE_SCANNERS[*]}" echo "Paths: ${#SCAN_PATHS[@]}" echo "" printf '%s\n' "${SCAN_PATHS[@]}" echo "" echo "==========================================" echo "" } > "$SUMMARY_FILE" # Track completion SCANNERS_COMPLETED=0 TOTAL_SCANNERS=${#AVAILABLE_SCANNERS[@]} # Run each scanner for scanner in "${AVAILABLE_SCANNERS[@]}"; do SCANNER_NUM=$((SCANNERS_COMPLETED + 1)) echo "" echo "" echo "==========================================" echo -e "${CYAN}${BOLD}Scanner $SCANNER_NUM of $TOTAL_SCANNERS: ${scanner^}${NC}" echo "==========================================" echo "" log_message "Starting ${scanner} scan ($SCANNER_NUM/$TOTAL_SCANNERS)" { echo "Scanner: ${scanner^}" echo "Started: $(date)" echo "---" } >> "$SUMMARY_FILE" case "$scanner" in imunify) # ImunifyAV has built-in exclusions that prevent comprehensive system scanning # Only use ImunifyAV for user-focused scans (not full server scans) if [ "${#SCAN_PATHS[@]}" -eq 1 ] && [ "${SCAN_PATHS[0]}" = "/" ]; then echo "" echo "ℹ️ Skipping ImunifyAV for full server scan" echo " Reason: ImunifyAV has built-in exclusions that skip system directories" echo " ClamAV and Maldet will provide comprehensive coverage instead" echo "" log_message "ImunifyAV: Skipped (not suitable for full server scans - use ClamAV/Maldet instead)" { echo "⊘ ImunifyAV scan skipped (not suitable for full system scans)" } >> "$SUMMARY_FILE" continue fi SCAN_START=$(date +%s) log_message "ImunifyAV: Updating signatures" if ! imunify-antivirus update &>> "$LOG_DIR/imunify.log"; then log_message "WARNING: ImunifyAV update failed (continuing with existing signatures)" echo "⚠️ WARNING: Signature update failed, using existing signatures" fi log_message "ImunifyAV: Starting on-demand scan" # Use on-demand start with background monitoring for progress LAST_SCAN="" TOTAL_FILES_SCANNED=0 # For user-focused scans, use paths as-is local IMUNIFY_SCAN_PATHS=("${SCAN_PATHS[@]}") for path in "${IMUNIFY_SCAN_PATHS[@]}"; do if [ -d "$path" ]; then log_message "ImunifyAV: Scanning $path" echo "" echo " 📁 Scanning path: $path" echo " ⏳ Scanner: ImunifyAV (monitoring progress...)" echo "" # Start scan (ImunifyAV runs async, command returns immediately) imunify-antivirus malware on-demand start --path="$path" &>> "$LOG_DIR/imunify.log" START_EXIT=$? if [ $START_EXIT -ne 0 ]; then log_message "ERROR: ImunifyAV scan failed to start for $path (exit code: $START_EXIT)" echo " ✗ Scan failed to start for $path (check logs)" continue fi # Monitor progress by polling scan status # ImunifyAV runs scans asynchronously, we poll the status sleep 3 # Give scan time to initialize last_count=0 timeout_counter=0 max_timeout=7200 # 2 hour timeout scan_running=true while [ "$scan_running" = true ]; do # Get current scan status from most recent scan scan_info=$(imunify-antivirus malware on-demand list 2>/dev/null | tail -n +2 | head -1) if [ -n "$scan_info" ]; then completed_time=$(echo "$scan_info" | awk '{print $1}') # Field 1 is COMPLETED timestamp created_time=$(echo "$scan_info" | awk '{print $2}') # Field 2 is CREATED current_files=$(echo "$scan_info" | awk '{print $11}') # Field 11 is TOTAL current_status=$(echo "$scan_info" | awk '{print $7}') # Field 7 is SCAN_STATUS # Check if this is our scan (created after we started) if [[ "$created_time" =~ ^[0-9]+$ ]] && [ "$created_time" -ge "$SCAN_START" ]; then # Check if scan is complete (COMPLETED field has timestamp) if [ -n "$completed_time" ] && [ "$completed_time" != "COMPLETED" ] && [[ "$completed_time" =~ ^[0-9]+$ ]] && [ "$completed_time" -gt 0 ]; then scan_running=false echo "" # New line after progress log_message "ImunifyAV scan finished for $path (status: $current_status)" break fi # Update progress if file count changed if [[ "$current_files" =~ ^[0-9]+$ ]]; then if [ "$current_files" != "$last_count" ]; then elapsed=$(($(date +%s) - SCAN_START)) printf "\r Files scanned: %s | Elapsed: %s " \ "$current_files" "$(format_time $elapsed)" last_count=$current_files timeout_counter=0 fi fi fi fi sleep 3 timeout_counter=$((timeout_counter + 3)) if [ $timeout_counter -ge $max_timeout ]; then log_message "ERROR: ImunifyAV scan timed out after 2 hours for $path" echo -e "\n ⏱️ Scan timed out (exceeded 2 hour limit)" # Try to stop the scan imunify-antivirus malware on-demand stop --path="$path" &>/dev/null continue 2 fi done # Get final scan results LAST_SCAN=$(imunify-antivirus malware on-demand list 2>/dev/null | tail -n +2 | head -1) FILES_SCANNED=$(echo "$LAST_SCAN" | awk '{print $11}') if ! [[ "$FILES_SCANNED" =~ ^[0-9]+$ ]]; then FILES_SCANNED=0 fi TOTAL_FILES_SCANNED=$((TOTAL_FILES_SCANNED + FILES_SCANNED)) echo " ✓ Scanned $FILES_SCANNED files in this path" fi done FILES_SCANNED=$TOTAL_FILES_SCANNED # Extract malicious file count # Skip header line and count data rows, or use TOTAL_MALICIOUS from most recent scan if [ -n "$LAST_SCAN" ]; then IMUNIFY_INFECTED=$(echo "$LAST_SCAN" | awk '{print $12}') else IMUNIFY_INFECTED=0 fi # Verify we got a valid number, otherwise try malicious list if ! [[ "$IMUNIFY_INFECTED" =~ ^[0-9]+$ ]]; then IMUNIFY_INFECTED=$(imunify-antivirus malware malicious list 2>/dev/null | tail -n +2 | wc -l || echo 0) fi SCAN_END=$(date +%s) DURATION=$((SCAN_END - SCAN_START)) echo " ⏱️ Duration: ${DURATION}s" echo "" echo "✓ ImunifyAV scan complete - Found: $IMUNIFY_INFECTED | Duration: ${DURATION}s" | tee -a "$SUMMARY_FILE" log_message "ImunifyAV: Scan complete - $IMUNIFY_INFECTED malicious files in ${DURATION}s" ;; clamav) SCAN_START=$(date +%s) if command -v freshclam &>/dev/null; then log_message "ClamAV: Updating signatures" if ! freshclam &>> "$LOG_DIR/clamav.log"; then log_message "WARNING: ClamAV signature update failed (continuing with existing signatures)" echo "⚠️ WARNING: Signature update failed, using existing signatures" fi fi log_message "ClamAV: Starting scan with activity monitoring" echo "" echo " 📁 Scanning path(s): ${SCAN_PATHS[*]}" echo " ⏳ Scanner: ClamAV (comprehensive virus scan...)" echo "" # ClamAV returns 1 if infected files found, 0 if clean, >1 for errors # Run in background with timeout (2 hours) and activity monitoring timeout 7200 clamscan --infected --recursive "${SCAN_PATHS[@]}" &>> "$LOG_DIR/clamav.log" & CLAM_PID=$! # Monitor activity by watching log file growth last_size=0 spin_chars='⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏' spin_index=0 stall_counter=0 while kill -0 $CLAM_PID 2>/dev/null; do # Get current log size and file count from log if [ -f "$LOG_DIR/clamav.log" ]; then current_size=$(stat -c%s "$LOG_DIR/clamav.log" 2>/dev/null || echo 0) # Try to get current file being scanned current_file=$(tail -1 "$LOG_DIR/clamav.log" 2>/dev/null | grep -o '/[^:]*' | head -1) if [ -n "$current_file" ]; then filename=$(basename "$current_file" 2>/dev/null || echo "...") elapsed=$(($(date +%s) - SCAN_START)) spin_char="${spin_chars:$spin_index:1}" printf "\r Scanning... %s | Last file: %s | Elapsed: %s " \ "$spin_char" "${filename:0:40}" "$(format_time $elapsed)" else elapsed=$(($(date +%s) - SCAN_START)) spin_char="${spin_chars:$spin_index:1}" printf "\r Scanning... %s | Elapsed: %s " "$spin_char" "$(format_time $elapsed)" fi # Check for stalled scan (no log growth in 60 seconds) if [ "$current_size" -eq "$last_size" ]; then stall_counter=$((stall_counter + 1)) if [ $stall_counter -eq 300 ]; then # 60 seconds (300 * 0.2s) - log only once log_message "WARNING: ClamAV scan appears stalled (no activity for 60s)" fi else stall_counter=0 fi last_size=$current_size fi spin_index=$(( (spin_index + 1) % 10 )) sleep 0.2 done # Wait for scan to complete and get exit code wait $CLAM_PID CLAM_EXIT=$? echo "" # New line after spinner if [ "$CLAM_EXIT" -eq 124 ]; then log_message "ERROR: ClamAV scan timed out after 2 hours" echo " ⏱️ Scan timed out (exceeded 2 hour limit)" echo "ClamAV scan timed out" >> "$SUMMARY_FILE" SCAN_END=$(date +%s) DURATION=$((SCAN_END - SCAN_START)) echo "" continue elif [ "$CLAM_EXIT" -gt 1 ]; then log_message "ERROR: ClamAV scan failed with exit code $CLAM_EXIT" echo " ✗ Scan failed (exit code: $CLAM_EXIT) - check logs" echo "ClamAV scan failed (exit code: $CLAM_EXIT)" >> "$SUMMARY_FILE" SCAN_END=$(date +%s) DURATION=$((SCAN_END - SCAN_START)) echo "" continue fi # Extract infected files grep "FOUND" "$LOG_DIR/clamav.log" | cut -d: -f1 >> "$INFECTED_LIST" 2>/dev/null # Get scan stats from log FILES_SCANNED=$(grep "Scanned files:" "$LOG_DIR/clamav.log" | tail -1 | awk '{print $3}') CLAM_INFECTED=$(grep -c "FOUND" "$LOG_DIR/clamav.log" 2>/dev/null || echo 0) # Validate numbers if ! [[ "$FILES_SCANNED" =~ ^[0-9]+$ ]]; then FILES_SCANNED=0 fi SCAN_END=$(date +%s) DURATION=$((SCAN_END - SCAN_START)) echo " ✓ Scanned $FILES_SCANNED files" echo " ⏱️ Duration: ${DURATION}s" echo "" echo "✓ ClamAV scan complete - Found: $CLAM_INFECTED | Duration: ${DURATION}s" | tee -a "$SUMMARY_FILE" log_message "ClamAV: Scan complete - $CLAM_INFECTED infected files in ${DURATION}s" ;; maldet) SCAN_START=$(date +%s) log_message "Maldet: Updating signatures" if ! maldet -u &>> "$LOG_DIR/maldet.log"; then log_message "WARNING: Maldet signature update failed (continuing with existing signatures)" echo "⚠️ WARNING: Signature update failed, using existing signatures" fi log_message "Maldet: Starting scan with live progress" echo "" echo " 📁 Scanning path(s): ${SCAN_PATHS[*]}" echo " ⏳ Scanner: Maldet/LMD (Linux-specific malware detection...)" echo "" # Scan each path individually with -a (scan-all) flag # Note: -a flag scans all files regardless of modification time # Cannot combine -a with -f (file-list), so we loop through paths MALDET_EXIT=0 TOTAL_MALDET_FILES=0 TOTAL_MALDET_HITS=0 for path in "${SCAN_PATHS[@]}"; do if [ ! -d "$path" ]; then log_message "Maldet: Skipping non-existent path: $path" continue fi log_message "Maldet: Scanning $path with -a (all files)" # Run with -a (scan-all) for comprehensive scanning # Timeout after 2 hours per path timeout 7200 maldet -b -a "$path" &>> "$LOG_DIR/maldet.log" local exit_code=$? if [ $exit_code -ne 0 ]; then MALDET_EXIT=$exit_code fi # Give scan a moment to complete sleep 2 done echo "" # New line after progress if [ "$MALDET_EXIT" -eq 124 ]; then log_message "ERROR: Maldet scan timed out after 2 hours" echo " ⏱️ Scan timed out (exceeded 2 hour limit)" echo "Maldet scan timed out" >> "$SUMMARY_FILE" SCAN_END=$(date +%s) DURATION=$((SCAN_END - SCAN_START)) echo "" continue elif [ "$MALDET_EXIT" -ne 0 ]; then log_message "ERROR: Maldet scan failed with exit code $MALDET_EXIT" echo " ✗ Scan failed (exit code: $MALDET_EXIT) - check logs" echo "Maldet scan failed (exit code: $MALDET_EXIT)" >> "$SUMMARY_FILE" SCAN_END=$(date +%s) DURATION=$((SCAN_END - SCAN_START)) echo "" continue fi # Extract scan results from event log (more reliable than parsing output) # Maldet logs to /usr/local/maldetect/logs/event_log FILES_SCANNED=$(grep "scan completed" /usr/local/maldetect/logs/event_log | tail -1 | grep -oP 'files \K[0-9]+' || echo 0) MALDET_HITS=$(grep "scan completed" /usr/local/maldetect/logs/event_log | tail -1 | grep -oP 'malware hits \K[0-9]+' || echo 0) # Validate numbers if ! [[ "$FILES_SCANNED" =~ ^[0-9]+$ ]]; then FILES_SCANNED=0 fi if ! [[ "$MALDET_HITS" =~ ^[0-9]+$ ]]; then MALDET_HITS=0 fi SCAN_END=$(date +%s) DURATION=$((SCAN_END - SCAN_START)) echo " ✓ Scanned $FILES_SCANNED files" echo " ⏱️ Duration: ${DURATION}s" echo "" echo "✓ Maldet scan complete - Found: ${MALDET_HITS:-0} | Duration: ${DURATION}s" | tee -a "$SUMMARY_FILE" log_message "Maldet: Scan complete - ${MALDET_HITS:-0} hits in ${DURATION}s" ;; rkhunter) SCAN_START=$(date +%s) log_message "RKHunter: Updating definitions" if ! rkhunter --update &>> "$LOG_DIR/rkhunter.log"; then log_message "WARNING: RKHunter update failed (continuing with existing definitions)" echo "⚠️ WARNING: Definition update failed, using existing definitions" fi log_message "RKHunter: Starting scan with live test display" echo "" echo " 🔍 System scan: Checking for rootkits, backdoors, exploits" echo " ⏳ Scanner: Rootkit Hunter (system-wide integrity check...)" echo "" # Run with timeout (30 minutes, RKHunter is usually fast) # Show test names as they run timeout 1800 rkhunter --check --skip-keypress --report-warnings-only 2>&1 | tee -a "$LOG_DIR/rkhunter.log" | \ while IFS= read -r line; do # Parse test names: "Checking for..." or "Testing..." if [[ "$line" =~ ^Checking\ for\ (.+)$ ]] || [[ "$line" =~ ^Testing\ (.+)$ ]]; then test_name="${BASH_REMATCH[1]}" printf "\r → %-60s" "${test_name:0:60}" elif [[ "$line" =~ ^Scanning\ (.+)$ ]]; then scan_item="${BASH_REMATCH[1]}" printf "\r → Scanning: %-50s" "${scan_item:0:50}" fi done RKH_EXIT=$? echo "" # New line after test display if [ "$RKH_EXIT" -eq 124 ]; then log_message "ERROR: RKHunter scan timed out after 30 minutes" echo " ⏱️ Scan timed out (exceeded 30 minute limit)" echo "RKHunter scan timed out" >> "$SUMMARY_FILE" SCAN_END=$(date +%s) DURATION=$((SCAN_END - SCAN_START)) echo "" continue elif [ "$RKH_EXIT" -gt 1 ] && [ "$RKH_EXIT" -ne 127 ]; then log_message "WARNING: RKHunter scan completed with exit code $RKH_EXIT" echo " ⚠️ Scan completed with warnings (exit code: $RKH_EXIT)" fi # Extract warnings RKH_WARNINGS=$(grep -c "Warning:" "$LOG_DIR/rkhunter.log" 2>/dev/null || echo 0) # Extract any rootkits found grep "Rootkit" "$LOG_DIR/rkhunter.log" | grep -i "found" >> "$INFECTED_LIST" 2>/dev/null SCAN_END=$(date +%s) DURATION=$((SCAN_END - SCAN_START)) echo " ✓ System integrity check complete" echo " ⏱️ Duration: ${DURATION}s" echo "" echo "✓ RKHunter scan complete - Warnings: $RKH_WARNINGS | Duration: ${DURATION}s" | tee -a "$SUMMARY_FILE" log_message "RKHunter: Scan complete - $RKH_WARNINGS warnings in ${DURATION}s" ;; esac echo "" | tee -a "$SUMMARY_FILE" ((SCANNERS_COMPLETED++)) # Wait between scanners if [ ${SCANNERS_COMPLETED:-0} -lt $TOTAL_SCANNERS ]; then echo "Waiting 3 seconds before next scanner..." sleep 3 fi done # Finalize report with consolidated summary { echo "==========================================" echo "Scan Session Complete" echo "Completed: $(date)" echo "==========================================" echo "" # Consolidated Scanner Results Table echo "SCANNER RESULTS SUMMARY:" echo "────────────────────────────────────────" # ImunifyAV results if echo "${AVAILABLE_SCANNERS[*]}" | grep -q "imunify"; then IMUNIFY_COUNT=$(grep -o "ImunifyAV scan complete - Found: [0-9]*" "$SUMMARY_FILE" | grep -o "[0-9]*$" || echo "N/A") printf "%-20s %s\n" "ImunifyAV:" "$IMUNIFY_COUNT threats detected" fi # ClamAV results if echo "${AVAILABLE_SCANNERS[*]}" | grep -q "clamav"; then CLAM_COUNT=$(grep -o "ClamAV scan complete - Found: [0-9]*" "$SUMMARY_FILE" | grep -o "[0-9]*$" || echo "N/A") printf "%-20s %s\n" "ClamAV:" "$CLAM_COUNT infected files" fi # Maldet results if echo "${AVAILABLE_SCANNERS[*]}" | grep -q "maldet"; then printf "%-20s %s\n" "Maldet:" "Scan complete (check logs)" fi # RKHunter results if echo "${AVAILABLE_SCANNERS[*]}" | grep -q "rkhunter"; then RKH_COUNT=$(grep -o "RKHunter scan complete - Warnings: [0-9]*" "$SUMMARY_FILE" | grep -o "[0-9]*$" || echo "N/A") printf "%-20s %s\n" "Rootkit Hunter:" "$RKH_COUNT warnings" fi echo "────────────────────────────────────────" echo "" if [ -f "$INFECTED_LIST" ] && [ -s "$INFECTED_LIST" ]; then echo "⚠️ INFECTED FILES DETECTED:" echo "" sort -u "$INFECTED_LIST" echo "" echo "ACTION REQUIRED: Review and quarantine/remove infected files" echo "" # IP Reputation Integration: Flag IPs that uploaded malware echo "────────────────────────────────────────" echo "Analyzing upload sources..." echo "────────────────────────────────────────" # Correlate infected files with Apache logs to find uploading IPs flagged_ips=0 while read -r infected_file; do # Extract file path components filename=$(basename "$infected_file") filepath=$(dirname "$infected_file") # Try to find corresponding Apache access logs # Look for POST requests to the directory containing the infected file # Use system-detected log directory with control panel-specific search if [ "$CONTROL_PANEL" = "interworx" ]; then # InterWorx: Search /home/*/var/*/logs/transfer.log (VERIFIED: uses 'transfer.log') # Search last 7 days of logs for POST requests to this path find /home/*/var/*/logs -type f -name 'transfer.log' 2>/dev/null | while read -r logfile; do # Check if this log corresponds to the domain/user grep -h "POST.*${filepath}" "$logfile" 2>/dev/null | tail -20 | while read -r logline; do # Extract IP from Apache log line ip=$(echo "$logline" | awk '{print $1}') if [ -n "$ip" ] && [[ "$ip" =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$ ]]; then # Flag this IP in reputation database if type flag_ip_attack &>/dev/null; then flag_ip_attack "$ip" "RCE" 25 "Malware scanner: Uploaded $filename" >/dev/null 2>&1 echo " → Flagged IP: $ip (uploaded to $filepath)" >> "$LOG_DIR/flagged_ips.log" ((flagged_ips++)) fi fi done done elif [ "$CONTROL_PANEL" = "plesk" ]; then # Plesk: Search /var/www/vhosts/*/logs/access*log # Plesk stores logs in /var/www/vhosts/domain.com/logs/access_log or access_ssl_log find /var/www/vhosts/*/logs -type f \( -name 'access_log' -o -name 'access_ssl_log' \) 2>/dev/null | while read -r logfile; do # Check if this log corresponds to the domain/user grep -h "POST.*${filepath}" "$logfile" 2>/dev/null | tail -20 | while read -r logline; do # Extract IP from Apache log line ip=$(echo "$logline" | awk '{print $1}') if [ -n "$ip" ] && [[ "$ip" =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$ ]]; then # Flag this IP in reputation database if type flag_ip_attack &>/dev/null; then flag_ip_attack "$ip" "RCE" 25 "Malware scanner: Uploaded $filename" >/dev/null 2>&1 echo " → Flagged IP: $ip (uploaded to $filepath)" >> "$LOG_DIR/flagged_ips.log" ((flagged_ips++)) fi fi done done elif [ "$CONTROL_PANEL" = "cpanel" ]; then # cPanel: Search domlogs directory # cPanel stores logs as domain.com, domain.net, etc. in /var/log/apache2/domlogs/ if [ -n "$SYS_LOG_DIR" ] && [ -d "$SYS_LOG_DIR" ]; then find "$SYS_LOG_DIR" -type f \( -name '*.com' -o -name '*.net' -o -name '*.org' -o -name '*.info' -o -name '*.biz' \) 2>/dev/null | while read -r logfile; do # Check if this log corresponds to the domain/user grep -h "POST.*${filepath}" "$logfile" 2>/dev/null | tail -20 | while read -r logline; do # Extract IP from Apache log line ip=$(echo "$logline" | awk '{print $1}') if [ -n "$ip" ] && [[ "$ip" =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$ ]]; then # Flag this IP in reputation database if type flag_ip_attack &>/dev/null; then flag_ip_attack "$ip" "RCE" 25 "Malware scanner: Uploaded $filename" >/dev/null 2>&1 echo " → Flagged IP: $ip (uploaded to $filepath)" >> "$LOG_DIR/flagged_ips.log" ((flagged_ips++)) fi fi done done fi fi done < <(sort -u "$INFECTED_LIST" | head -20) # Limit to first 20 files to avoid long processing if [ "${flagged_ips:-0}" -gt 0 ]; then echo "✓ Flagged $flagged_ips IPs in reputation database" echo " (See $LOG_DIR/flagged_ips.log for details)" else echo " No upload IPs identified (files may be older than log retention)" fi echo "" else echo "✓ No infected files detected by automated scan." echo "" echo "Review individual scanner logs for detailed information:" echo " • ImunifyAV: $LOG_DIR/imunify.log" echo " • ClamAV: $LOG_DIR/clamav.log" echo " • Maldet: $LOG_DIR/maldet.log" echo " • RKHunter: $LOG_DIR/rkhunter.log" fi } >> "$SUMMARY_FILE" # Validate scan results log_message "Validating scan results..." validation_issues=0 # Check that each scanner produced output for scanner in "${AVAILABLE_SCANNERS[@]}"; do case "$scanner" in imunify) if [ ! -s "$LOG_DIR/imunify.log" ]; then log_message "WARNING: ImunifyAV log file is empty or missing" echo "⚠️ WARNING: ImunifyAV scan may not have completed properly" >> "$SUMMARY_FILE" ((validation_issues++)) fi ;; clamav) if [ ! -s "$LOG_DIR/clamav.log" ]; then log_message "WARNING: ClamAV log file is empty or missing" echo "⚠️ WARNING: ClamAV scan may not have completed properly" >> "$SUMMARY_FILE" ((validation_issues++)) else # Verify ClamAV reached the summary line if ! grep -q "Scanned files:" "$LOG_DIR/clamav.log"; then log_message "WARNING: ClamAV scan may have been interrupted (no summary found)" echo "⚠️ WARNING: ClamAV scan may have been interrupted" >> "$SUMMARY_FILE" ((validation_issues++)) fi fi ;; maldet) if [ ! -s "$LOG_DIR/maldet.log" ]; then log_message "WARNING: Maldet log file is empty or missing" echo "⚠️ WARNING: Maldet scan may not have completed properly" >> "$SUMMARY_FILE" ((validation_issues++)) fi ;; rkhunter) if [ ! -s "$LOG_DIR/rkhunter.log" ]; then log_message "WARNING: RKHunter log file is empty or missing" echo "⚠️ WARNING: RKHunter scan may not have completed properly" >> "$SUMMARY_FILE" ((validation_issues++)) fi ;; esac done if [ $validation_issues -eq 0 ]; then log_message "All scans completed successfully - validation passed" echo "" >> "$SUMMARY_FILE" echo "✓ Scan Validation: All scanners completed successfully" >> "$SUMMARY_FILE" else log_message "WARNING: $validation_issues validation issue(s) found - review logs carefully" echo "" >> "$SUMMARY_FILE" echo "⚠️ Scan Validation: $validation_issues issue(s) found - review logs" >> "$SUMMARY_FILE" fi # Generate client report automatically log_message "Generating client-facing security report" generate_client_report "$SCAN_DIR" > /dev/null 2>&1 # Display completion clear echo "==========================================" echo -e "${GREEN}Malware Scan Complete!${NC}" echo "==========================================" echo "Session: $(basename $SCAN_DIR)" echo "Completed: $(date)" echo "" echo "Results saved to:" echo " Summary: $SUMMARY_FILE" echo " Logs: $LOG_DIR/" echo "" echo -e "${CYAN}Client Report (copy/paste for tickets):${NC}" echo " $RESULTS_DIR/client_report.txt" echo "" # Show summary cat "$SUMMARY_FILE" echo "" echo "==========================================" echo "" echo -e "${CYAN}TIP:${NC} To view the client-friendly report:" echo " cat $RESULTS_DIR/client_report.txt" echo "" # Prompt for cleanup (RKHunter cleanup handled by trap) read -p "Delete scan script? (Logs and results will be preserved) (yes/no): " cleanup_choice if [ "$cleanup_choice" = "yes" ]; then log_message "User requested cleanup - deleting scan script" echo "" echo "Removing scan script..." rm -f "$SCAN_DIR/scan.sh" echo -e "${GREEN}✓ Scan script deleted${NC}" echo "" echo "Results preserved at: $SCAN_DIR" echo "" else log_message "User chose to keep scan script" echo "" echo "Scan script and results preserved at: $SCAN_DIR" echo "" fi echo "You can:" echo " • Review logs: ls $LOG_DIR" echo " • View summary: cat $SUMMARY_FILE" echo " • Delete scan directory manually: rm -rf $SCAN_DIR" echo "" echo "Press Ctrl+A then D to detach from this screen session," echo "or press Enter to open an interactive shell in this session..." echo "" read -t 30 -p "" # Keep screen session alive with an interactive shell echo "" echo "Opening interactive shell. Type 'exit' to close this screen session." echo "" log_message "Scan session ended - opening interactive shell" exec bash STANDALONE_EOF # Replace placeholder with actual paths paths_declaration="SCAN_PATHS=(" for path in "${scan_paths[@]}"; do paths_declaration+="\"$path\" " done paths_declaration+=")" sed -i "s|PLACEHOLDER_SCAN_PATHS|$paths_declaration|" "$session_dir/scan.sh" # Make executable chmod +x "$session_dir/scan.sh" # Check if screen is installed if ! command -v screen &>/dev/null; then echo -e "${YELLOW}Warning: 'screen' not installed${NC}" echo "" echo "Screen allows you to detach from the scan session." echo "" echo "Options:" echo " 1. Auto-install screen (recommended)" echo " 2. Use nohup fallback (run in background without screen)" echo " 3. Cancel" echo "" read -p "Select option: " screen_option case "$screen_option" in 1) echo "" echo "Installing screen..." if command -v yum &>/dev/null; then yum install -y screen elif command -v apt-get &>/dev/null; then apt-get update && apt-get install -y screen else echo -e "${RED}Unable to auto-install. Install manually: yum install screen${NC}" read -p "Press Enter to continue..." return 1 fi if ! command -v screen &>/dev/null; then echo -e "${RED}Installation failed${NC}" read -p "Press Enter to continue..." return 1 fi echo -e "${GREEN}✓ Screen installed successfully${NC}" echo "" ;; 2) # Use nohup fallback echo "" echo "Launching scan with nohup (background mode)..." nohup bash "$session_dir/scan.sh" > "$session_dir/logs/nohup.out" 2>&1 & scan_pid=$! sleep 1 if ps -p $scan_pid > /dev/null 2>&1; then echo "" echo -e "${GREEN}✓ Standalone scanner started successfully!${NC}" echo "" echo "Session ID: $session_id" echo "Process ID: $scan_pid" echo "Results directory: $session_dir/results/" echo "" echo -e "${CYAN}Monitor the scan:${NC}" echo " tail -f $session_dir/logs/session.log" echo "" echo -e "${CYAN}Check if still running:${NC}" echo " ps -p $scan_pid" echo "" echo -e "${GREEN}You can now safely delete the toolkit.${NC}" echo -e "${GREEN}The scan will continue running independently.${NC}" echo "" # Store session info in reference database store_reference "malware_standalone_latest" "$session_id" store_reference "malware_standalone_${session_id}_dir" "$session_dir" store_reference "malware_standalone_${session_id}_pid" "$scan_pid" read -p "Press Enter to continue..." return 0 else echo -e "${RED}Failed to start scan${NC}" echo "Run manually: bash $session_dir/scan.sh" read -p "Press Enter to continue..." return 1 fi ;; 3) echo "Cancelled." read -p "Press Enter to continue..." return 0 ;; *) echo -e "${RED}Invalid option${NC}" read -p "Press Enter to continue..." return 1 ;; esac fi # Launch in screen session echo "Launching scan in screen session..." screen -dmS "$session_id" bash "$session_dir/scan.sh" sleep 1 # Verify screen started if screen -list | grep -q "$session_id"; then echo "" echo -e "${GREEN}✓ Standalone scanner started successfully!${NC}" echo "" echo "Session ID: $session_id" echo "Screen session: $session_id" echo "Results directory: $session_dir/results/" echo "" echo -e "${CYAN}Monitor the scan:${NC}" echo " screen -r $session_id" echo "" echo -e "${CYAN}Check progress:${NC}" echo " tail -f $session_dir/logs/session.log" echo "" echo -e "${CYAN}Detach from screen:${NC}" echo " Press: Ctrl+A then D" echo "" echo -e "${GREEN}You can now safely delete the toolkit.${NC}" echo -e "${GREEN}The scan will continue running independently.${NC}" echo "" # Store session info in reference database store_reference "malware_standalone_latest" "$session_id" store_reference "malware_standalone_${session_id}_dir" "$session_dir" else echo -e "${RED}Failed to start screen session${NC}" echo "Run manually: bash $session_dir/scan.sh" fi read -p "Press Enter to continue..." } # Compare results from multiple scanners compare_scan_results() { echo "" print_header "Compare Scanner Results" # Get latest multiscan session local latest_session=$(get_reference "malware_multiscan_latest") if [ -z "$latest_session" ]; then echo "No multi-scanner sessions found." echo "" echo "Run a scan with 'All Available Scanners' option first." read -p "Press Enter to continue..." return fi local report_file=$(get_reference "malware_multiscan_${latest_session}") if [ -f "$report_file" ]; then echo "Latest multi-scanner session: $latest_session" echo "" less "$report_file" else echo "Report file not found: $report_file" fi echo "" read -p "Press Enter to continue..." } # Launch standalone scanner menu launch_standalone_scanner_menu() { local preset_scope="$1" # Optional: server, user, domain, custom echo "" print_header "Launch Standalone Scanner" echo "This will create a self-contained scanner in /opt/ that runs" echo "independently. You can safely delete the toolkit after launching." echo "" if ! detect_control_panel; then read -p "Press Enter to continue..." return 1 fi echo "Control Panel: ${CONTROL_PANEL^}" echo "Available Scanners: ${available_scanners[*]}" echo "" local scope_choice local scan_paths=() local scan_description="" # If preset scope provided, use it; otherwise show menu if [ -n "$preset_scope" ]; then case "$preset_scope" in server) scope_choice=1 ;; all_users) scope_choice=2 ;; user) scope_choice=3 ;; domain) scope_choice=4 ;; custom) scope_choice=5 ;; *) scope_choice=0 ;; esac else echo "Select scan scope:" echo " 1. Entire server (scan from / - WARNING: may take several hours)" echo " 2. Specific user account" echo " 3. Specific domain" echo " 4. Custom path" echo " 0. Cancel" echo "" read -p "Select option: " scope_choice fi case $scope_choice in 1) # Entire server scan_paths=("/") scan_description="full server scan" echo "" echo -e "${YELLOW}WARNING: Full server scan from /${NC}" echo "This will scan the ENTIRE filesystem including:" echo " • All user directories" echo " • System files" echo " • Application files" echo "" echo "This scan may take several hours and use significant resources." echo "" read -p "Are you sure you want to proceed? (yes/no): " confirm_full_scan if [ "$confirm_full_scan" != "yes" ]; then echo "Cancelled." read -p "Press Enter to continue..." return 0 fi echo "" echo "Scan scope: Entire server from /" ;; 2) # All user accounts echo "" echo "Scanning all user home directories..." # Determine user base directory based on control panel local user_base_dir case "$CONTROL_PANEL" in plesk) user_base_dir="/var/www/vhosts" ;; cpanel|interworx|standalone) user_base_dir="/home" ;; *) user_base_dir="/home" ;; esac # Add the user base directory to scan paths scan_paths=("$user_base_dir") scan_description="all user accounts in $user_base_dir" echo "Control Panel: ${CONTROL_PANEL^}" echo "User directory: $user_base_dir" echo "Scan scope: All user home directories" ;; 3) # Specific user echo "" echo "Available users:" select_user_interactive "Select user account to scan" if [ -z "$SELECTED_USER" ]; then echo "No user selected." read -p "Press Enter to continue..." return 1 fi # Get user's docroots for docroot in "${sanitized_docroot[@]}"; do if [[ "$docroot" == *"/$SELECTED_USER/"* ]]; then scan_paths+=("$docroot") fi done if [ ${#scan_paths[@]} -eq 0 ]; then echo -e "${RED}No docroots found for user: $SELECTED_USER${NC}" read -p "Press Enter to continue..." return 1 fi scan_description="user $SELECTED_USER" echo "Found ${#scan_paths[@]} docroots for $SELECTED_USER" ;; 4) # Specific domain echo "" read -p "Enter domain name: " domain if [ -z "$domain" ]; then echo "No domain entered." read -p "Press Enter to continue..." return 1 fi # Find docroot for domain for docroot in "${sanitized_docroot[@]}"; do if [[ "$docroot" == *"/$domain"* ]] || [[ "$docroot" == *"/$domain/"* ]]; then scan_paths+=("$docroot") fi done if [ ${#scan_paths[@]} -eq 0 ]; then echo -e "${RED}No docroot found for domain: $domain${NC}" read -p "Press Enter to continue..." return 1 fi scan_description="domain $domain" echo "Found docroot: ${scan_paths[0]}" ;; 5) # Custom path echo "" read -p "Enter path to scan: " custom_path if [ -z "$custom_path" ]; then echo "No path entered." read -p "Press Enter to continue..." return 1 fi if [ ! -d "$custom_path" ]; then echo -e "${RED}Path does not exist: $custom_path${NC}" read -p "Press Enter to continue..." return 1 fi scan_paths=("$custom_path") scan_description="custom path $custom_path" ;; 0) return 0 ;; *) echo -e "${RED}Invalid option${NC}" sleep 1 return 1 ;; esac # Confirm before generating echo "" echo -e "${YELLOW}Ready to generate standalone scanner${NC}" echo "Scope: $scan_description" echo "Paths: ${#scan_paths[@]}" echo "Scanners: ${available_scanners[*]}" echo "" read -p "Generate and launch? (yes/no): " confirm if [ "$confirm" != "yes" ]; then echo "Cancelled." read -p "Press Enter to continue..." return 0 fi # Generate and launch standalone scanner generate_standalone_scanner "${scan_paths[@]}" } # Check status of all standalone scanners check_standalone_status() { echo "" print_header "Standalone Scanner Status" # Find all malware-* directories in /opt local standalone_dirs=($(find /opt -maxdepth 1 -type d -name "malware-*" 2>/dev/null | sort -r)) if [ ${#standalone_dirs[@]} -eq 0 ]; then echo "No standalone scanner sessions found." echo "" read -p "Press Enter to continue..." return 0 fi echo "Active Sessions:" echo "" local running_count=0 local completed_count=0 local error_count=0 for dir in "${standalone_dirs[@]}"; do local session_name=$(basename "$dir") # Check if still running by looking for bash process executing scan.sh # Use pgrep with exact match to avoid false positives from viewers/editors if pgrep -f "bash $dir/scan.sh" > /dev/null 2>&1 || [ -f "$dir/.scan_running" ]; then echo -e " ${GREEN}●${NC} $session_name [RUNNING]" ((running_count++)) # Show progress if available if [ -f "$dir/logs/session.log" ]; then local last_log=$(tail -1 "$dir/logs/session.log" 2>/dev/null) echo " Latest: $last_log" fi elif [ -f "$dir/results/summary.txt" ]; then # Check if completed successfully if grep -q "Multi-Scanner Session Complete\|Scan session ended" "$dir/results/summary.txt" 2>/dev/null; then echo -e " ${CYAN}✓${NC} $session_name [COMPLETED]" ((completed_count++)) # Show infected count if available if [ -f "$dir/results/infected_files.txt" ] && [ -s "$dir/results/infected_files.txt" ]; then local infected_count=$(wc -l < "$dir/results/infected_files.txt") echo -e " Found: ${RED}$infected_count infected files${NC}" fi else echo -e " ${RED}✗${NC} $session_name [ERROR/INCOMPLETE]" ((error_count++)) fi else echo -e " ${YELLOW}?${NC} $session_name [UNKNOWN - no results yet]" fi echo "" done echo "Summary:" echo " Running: $running_count" echo " Completed: $completed_count" echo " Errors: $error_count" echo " Total: ${#standalone_dirs[@]}" echo "" read -p "Press Enter to continue..." } # Delete standalone scanner sessions delete_standalone_sessions() { echo "" print_header "Delete Standalone Scanner Sessions" # Find all malware-* directories in /opt local standalone_dirs=($(find /opt -maxdepth 1 -type d -name "malware-*" 2>/dev/null | sort -r)) if [ ${#standalone_dirs[@]} -eq 0 ]; then echo "No standalone scanner sessions found." echo "" read -p "Press Enter to continue..." return 0 fi echo "Available sessions:" echo "" # List sessions with status local i=1 for dir in "${standalone_dirs[@]}"; do local session_name=$(basename "$dir") local status="completed" if pgrep -f "bash $dir/scan.sh" > /dev/null 2>&1 || [ -f "$dir/.scan_running" ]; then status="${GREEN}running${NC}" fi echo -e " $i. $session_name [$status]" ((i++)) done echo "" echo " A. Delete all completed sessions" echo " 0. Cancel" echo "" read -p "Select session to delete (or A for all completed): " delete_choice case "$delete_choice" in 0) return 0 ;; [Aa]) # Delete all completed sessions echo "" local deleted=0 for dir in "${standalone_dirs[@]}"; do if ! pgrep -f "$dir/scan.sh" > /dev/null 2>&1; then echo "Deleting: $(basename $dir)" rm -rf "$dir" ((deleted++)) fi done echo "" echo -e "${GREEN}✓ Deleted $deleted completed session(s)${NC}" ;; *) # Delete specific session # Validate numeric input if ! [[ "$delete_choice" =~ ^[0-9]+$ ]]; then echo -e "${RED}Invalid choice (must be a number)${NC}" read -p "Press Enter to continue..." return 1 fi if [ "$delete_choice" -lt 1 ] || [ "$delete_choice" -gt ${#standalone_dirs[@]} ]; then echo -e "${RED}Invalid choice (out of range)${NC}" read -p "Press Enter to continue..." return 1 fi local selected_dir="${standalone_dirs[$((delete_choice-1))]}" local session_name=$(basename "$selected_dir") # Check if running if pgrep -f "$selected_dir/scan.sh" > /dev/null 2>&1; then echo "" echo -e "${YELLOW}Warning: This scan is currently running!${NC}" read -p "Stop scan and delete? (yes/no): " confirm_running if [ "$confirm_running" = "yes" ]; then pkill -f "$selected_dir/scan.sh" sleep 1 rm -rf "$selected_dir" echo -e "${GREEN}✓ Stopped and deleted: $session_name${NC}" else echo "Cancelled." fi else echo "" read -p "Delete $session_name? (yes/no): " confirm_delete if [ "$confirm_delete" = "yes" ]; then rm -rf "$selected_dir" echo -e "${GREEN}✓ Deleted: $session_name${NC}" else echo "Cancelled." fi fi ;; esac echo "" read -p "Press Enter to continue..." } # Main scan menu show_scan_menu() { # Build reference database once for the entire menu session if command -v build_reference_database &>/dev/null; then echo "Building system reference database..." build_reference_database 2>/dev/null || true clear fi while true; do print_banner "Malware Scanner" echo "Available Scanners:" for scanner in "${available_scanners[@]}"; do echo " • ${scanner^}" done echo "" echo -e "${CYAN}Create New Scan:${NC}" echo " 1. Scan entire server (ClamAV, Maldet, RKHunter)" echo " 2. Scan all user accounts (All scanners - recommended)" echo " 3. Scan specific user account (All scanners)" echo " 4. Scan specific domain (All scanners)" echo " 5. Scan custom path (All scanners)" echo "" echo -e "${CYAN}Monitor & Manage:${NC}" echo " 6. Check scan status" echo " 7. View scan results" echo " 8. Delete scan sessions" echo "" echo -e "${CYAN}Configuration:${NC}" echo " 9. Install all scanners" echo " 10. Scanner settings" echo "" echo -e " ${RED}0.${NC} Back" echo "" read -p "Select option: " choice case $choice in 1) launch_standalone_scanner_menu "server" ;; 2) launch_standalone_scanner_menu "all_users" ;; 3) launch_standalone_scanner_menu "user" ;; 4) launch_standalone_scanner_menu "domain" ;; 5) launch_standalone_scanner_menu "custom" ;; 6) check_standalone_status ;; 7) view_scan_results ;; 8) delete_standalone_sessions ;; 9) install_all_scanners ;; 10) scanner_settings ;; 0) return 0 ;; *) echo -e "${RED}Invalid option${NC}"; sleep 1 ;; esac done } # View scan results view_scan_results() { echo "" print_header "Scan Results" echo "Select results to view:" echo " 1. Toolkit scan results" echo " 2. Standalone scanner results (/opt)" echo " 0. Back" echo "" read -p "Option: " result_type case "$result_type" in 1) # Toolkit scan results echo "" echo "Select scanner to view results:" local i=1 for scanner in "${available_scanners[@]}"; do echo " $i. ${scanner^}" ((i++)) done echo "" read -p "Scanner: " scanner_choice # Validate numeric input if ! [[ "$scanner_choice" =~ ^[0-9]+$ ]]; then echo -e "${RED}Invalid choice (must be a number)${NC}" read -p "Press Enter to continue..." return 1 fi if [ "$scanner_choice" -lt 1 ] || [ "$scanner_choice" -gt ${#available_scanners[@]} ]; then echo -e "${RED}Invalid choice (out of range)${NC}" read -p "Press Enter to continue..." return 1 fi local selected_scanner="${available_scanners[$((scanner_choice-1))]}" echo "" case "$selected_scanner" in imunify) echo "Recent ImunifyAV scans:" imunify-antivirus malware on-demand list --since $(date --date="7 days ago" '+%s') 2>/dev/null || echo "No scans found" ;; clamav) echo "Recent ClamAV scans:" find "$SCRIPT_DIR/logs/malware-scans" -name "clamav_*.log" -mtime -7 2>/dev/null | sort -r | head -5 || echo "No scans found" ;; maldet) echo "Recent Maldet scans:" maldet -l 2>/dev/null || echo "No scans found" ;; esac ;; 2) # Standalone scanner results echo "" echo "Standalone scanner sessions:" echo "" # Find all malware-* directories in /opt local standalone_dirs=($(find /opt -maxdepth 1 -type d -name "malware-*" 2>/dev/null | sort -r)) if [ ${#standalone_dirs[@]} -eq 0 ]; then echo "No standalone scanner sessions found in /opt" echo "" read -p "Press Enter to continue..." return 0 fi # List sessions local i=1 for dir in "${standalone_dirs[@]}"; do local session_name=$(basename "$dir") local scan_date=$(echo "$session_name" | sed 's/malware-//') # Check if still running local status="completed" if pgrep -f "$dir/scan.sh" > /dev/null 2>&1; then status="running" fi echo " $i. $session_name [$status]" ((i++)) done echo "" read -p "Select session (or 0 to cancel): " session_choice # Validate numeric input if ! [[ "$session_choice" =~ ^[0-9]+$ ]]; then echo -e "${RED}Invalid choice (must be a number)${NC}" read -p "Press Enter to continue..." return 1 fi if [ "$session_choice" = "0" ]; then return 0 fi if [ "$session_choice" -lt 1 ] || [ "$session_choice" -gt ${#standalone_dirs[@]} ]; then echo -e "${RED}Invalid choice (out of range)${NC}" read -p "Press Enter to continue..." return 1 fi local selected_dir="${standalone_dirs[$((session_choice-1))]}" echo "" echo "Session: $(basename $selected_dir)" echo "Location: $selected_dir" echo "" # Show results if [ -f "$selected_dir/results/summary.txt" ]; then echo "=== Summary ===" cat "$selected_dir/results/summary.txt" echo "" else echo "Summary not yet available (scan may still be running)" echo "" fi # Show infected files if any if [ -f "$selected_dir/results/infected_files.txt" ] && [ -s "$selected_dir/results/infected_files.txt" ]; then echo "=== Infected Files ===" cat "$selected_dir/results/infected_files.txt" echo "" fi # Show recent log entries if [ -f "$selected_dir/logs/session.log" ]; then echo "=== Recent Log Entries ===" tail -20 "$selected_dir/logs/session.log" echo "" fi echo "View full logs:" echo " tail -f $selected_dir/logs/session.log" echo "" # Offer to generate client report echo -e "${CYAN}Actions:${NC}" echo " 1. Generate client-facing security report" echo " 0. Back to menu" echo "" read -p "Select action (or press Enter to continue): " action_choice case "$action_choice" in 1) generate_client_report "$selected_dir" ;; 0|"") # Continue ;; *) echo -e "${RED}Invalid option${NC}" ;; esac ;; 0) return 0 ;; *) echo -e "${RED}Invalid option${NC}" ;; esac echo "" read -p "Press Enter to continue..." } # Scanner settings scanner_settings() { echo "" print_header "Scanner Settings" echo "Settings (placeholder for future enhancements):" echo " • Auto-quarantine infected files" echo " • Email notifications" echo " • Scheduled scans" echo " • Custom exclusions" echo "" echo "Coming soon..." read -p "Press Enter to continue..." } # Generate client-facing security report generate_client_report() { local scan_dir="$1" if [ ! -d "$scan_dir" ]; then echo -e "${RED}Scan directory not found${NC}" return 1 fi local summary_file="$scan_dir/results/summary.txt" local infected_file="$scan_dir/results/infected_files.txt" local clamav_log="$scan_dir/logs/clamav.log" local session_log="$scan_dir/logs/session.log" local report_file="$scan_dir/results/client_report.txt" if [ ! -f "$summary_file" ]; then echo -e "${RED}Summary file not found - scan may not be complete${NC}" return 1 fi # Extract scan info local session_name=$(basename "$scan_dir") local scan_date=$(grep "Started:" "$summary_file" | head -1 | sed 's/Started: //') local scan_paths=$(sed -n '/^Paths:/,/^$/p' "$summary_file" | tail -n +2 | grep -v "^$" | tr '\n' ', ' | sed 's/, $//') # Count threats local total_threats=0 local imunify_count=$(grep -o "ImunifyAV:.*[0-9]* threats" "$summary_file" | grep -o "[0-9]*" || echo "0") local clamav_count=$(grep -o "ClamAV:.*[0-9]* infected" "$summary_file" | grep -o "[0-9]*" || echo "0") local maldet_hits=$(grep -o "Maldet:.*[0-9]* hits" "$summary_file" | grep -o "[0-9]*" || echo "0") # Calculate total (only real malware, not rootkit warnings) total_threats=$((imunify_count + clamav_count + maldet_hits)) # Analyze infected files for false positives local real_threats=() local false_positives=() if [ -f "$infected_file" ] && [ -s "$infected_file" ]; then while IFS= read -r file; do # Check if likely false positive (logs, stats, cache) if [[ "$file" =~ /logs?/.*\.(log|gz|bz2)$ ]] || \ [[ "$file" =~ /awstats/ ]] || \ [[ "$file" =~ /tmp/.*\.txt$ ]] || \ [[ "$file" =~ \.log\.[0-9]+$ ]]; then false_positives+=("$file") else real_threats+=("$file") fi done < "$infected_file" fi # Generate report { echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo "SECURITY SCAN REPORT" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo "" echo "Scan Date: $scan_date" echo "Scan Coverage: $scan_paths" echo "" # Overall status echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo "OVERALL STATUS" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo "" if [ ${#real_threats[@]} -eq 0 ]; then echo "✅ NO ACTIVE MALWARE DETECTED" echo "" echo "Your server is clean. No malicious files were found in" echo "web-accessible directories or user content areas." else echo "⚠️ MALWARE DETECTED - ACTION REQUIRED" echo "" echo "Found ${#real_threats[@]} infected file(s) that require immediate attention." fi echo "" # Scan details echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo "SCAN DETAILS" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo "" echo "We performed a comprehensive security scan using multiple" echo "industry-standard malware detection engines:" echo "" echo " • ImunifyAV - Advanced threat detection" echo " • ClamAV - Open-source antivirus engine" echo " • Linux Maldet - Web malware specialist" echo " • Rootkit Hunter - System integrity checker" echo "" # Real threats section if [ ${#real_threats[@]} -gt 0 ]; then echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo "INFECTED FILES REQUIRING ATTENTION" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo "" for file in "${real_threats[@]}"; do echo "📁 $file" # Get detection details from ClamAV log if [ -f "$clamav_log" ]; then local detection=$(grep "$file" "$clamav_log" | grep "FOUND" | sed 's/.*: / /' || echo " Detection: Unknown signature") echo "$detection" fi echo "" done echo "RECOMMENDED ACTIONS:" echo "" echo "1. Review each file to confirm it is malicious" echo "2. Remove or quarantine infected files immediately" echo "3. Change all passwords (hosting, FTP, database, CMS admin)" echo "4. Review file upload functionality in web applications" echo "5. Update all web applications, plugins, and themes" echo "6. Check access logs for unauthorized access patterns" echo "" fi # False positives section if [ ${#false_positives[@]} -gt 0 ]; then echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo "INFORMATIONAL DETECTIONS (No Action Required)" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo "" echo "The following files triggered alerts but are likely false" echo "positives. These are log files that contain records of" echo "attack attempts against your server (which were blocked):" echo "" for file in "${false_positives[@]}"; do echo " • $file" done echo "" echo "These files are safe and contain evidence of your server" echo "correctly blocking malicious requests. No action needed." echo "" fi # Security observations echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo "SECURITY OBSERVATIONS" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo "" # Check for attack patterns in logs if [ -f "$clamav_log" ]; then local attack_signatures=$(grep -o "YARA\.[a-z0-9_]*" "$clamav_log" | sort -u | sed 's/YARA\.//' | head -5) if [ -n "$attack_signatures" ]; then echo "Attack Patterns Detected in Logs:" echo "" echo "$attack_signatures" | while read sig; do case "$sig" in *r57*|*c99*|*shell*) echo " • Web shell upload attempts (${sig})" ;; *sql*) echo " • SQL injection attempts (${sig})" ;; *) echo " • Malicious activity pattern: ${sig}" ;; esac done echo "" echo "These attack attempts were blocked by your server, but" echo "they indicate your site is being actively targeted." echo "" fi fi # General recommendations echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo "ONGOING SECURITY RECOMMENDATIONS" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo "" echo "To maintain server security, we recommend:" echo "" echo " ✓ Run malware scans monthly (or after any security incident)" echo " ✓ Keep all software updated (WordPress, plugins, PHP, etc.)" echo " ✓ Use strong, unique passwords for all accounts" echo " ✓ Enable automatic security updates where possible" echo " ✓ Review file permissions regularly" echo " ✓ Monitor server logs for suspicious activity" echo " ✓ Maintain regular backups (stored off-server)" echo "" # Footer echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo "TECHNICAL DETAILS" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo "" echo "Scan ID: $session_name" echo "Report Generated: $(date)" echo "" echo "For technical details and full scan logs, please contact" echo "your system administrator." echo "" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" } > "$report_file" # Display the report echo "" print_header "Client Security Report Generated" echo "" cat "$report_file" echo "" echo -e "${GREEN}Report saved to:${NC} $report_file" echo "" echo "You can now copy/paste this report into your support ticket." echo "" } # Main execution main() { if ! detect_scanners; then exit 1 fi show_scan_menu } # Run if executed directly if [ "${BASH_SOURCE[0]}" = "${0}" ]; then main "$@" fi