1 hour ago1 hr After recent CWP updates, some AlmaLinux 9.x servers may report false security alerts when running:sh /scripts/cwp_security_audit A typical false positive looks like this:------------------------------------------------------ [INFO] Auditing cwpsrv (PID: 767572) [OK] cwpsrv looks clean. ------------------------------------------------------ [INFO] Auditing php-fpm-cwp (PID: 710) [SECURITY ALERT] Unknown/Untrusted file: /usr/lib64/gconv/gconv-modules.cache Error:Can't add notification![SECURITY ALERT] Unauthorized port: php-fpm Error:Can't add notification!------------------------------------------------------ [INFO] Auditing apache (PID: 768077) [OK] apache looks clean. ------------------------------------------------------ [DONE] Security audit finished. In this case the warning is misleading. On AlmaLinux 9.x, the file:/usr/lib64/gconv/gconv-modules.cache is a normal system file used by the GNU C Library character conversion system. The original CWP audit script does not include /usr/lib64/gconv/ in the allowed library paths, so it incorrectly reports this file as unknown or untrusted.There is also a second parsing issue in the port audit section. The original script extracts listening ports using a simple awk -F':' expression against generic lsof output. In some cases this can incorrectly parse process-related text and produce an alert such as:[SECURITY ALERT] Unauthorized port: php-fpm Obviously, php-fpm is not a port number.What needs to be fixedThere are two small changes that solve the false positives.First, add this path to ALLOWED_LIB_PATHS:"/usr/lib64/gconv/" Second, replace the port audit line with a more precise lsof command that only checks TCP listening sockets:local CURRENT_PORTS=$(lsof -Pan -p $PID -iTCP -sTCP:LISTEN 2>/dev/null | awk 'NR>1 {split($9,a,":"); print a[length(a)]}') This avoids parsing unrelated lsof lines and prevents values like php-fpm from being treated as ports.Patched version of /scripts/cwp_security_auditBelow is the corrected version. It keeps the original logic but fixes the AlmaLinux 9.x false positives.#!/bin/bash # --- CONFIGURATION --- ALLOWED_LIB_PATHS=( "/usr/lib64/lib" "/usr/lib64/ld-" "/usr/local/ioncube/" "/usr/lib/locale/" "/usr/local/cwp/" "/usr/local/apache/modules/" "/usr/local/lib/" "/usr/lib64/gconv/" ) ALLOWED_BINARIES=( "/usr/local/cwpsrv/bin/cwpsrv" "/usr/local/cwp/php71/sbin/php-fpm" "/usr/local/apache/bin/httpd" ) ALLOWED_PORTS=("2030" "2031" "2082" "2083" "2086" "2087" "2095" "2096" "9000" "2302" "2304" "8181" "8443" "80" "443") # --- INITIALIZATION --- if ! command -v lsof &> /dev/null; then yum install -y lsof fi # --- FUNCTIONS --- check_process() { local PROC_NAME=$1 local SEARCH_PATTERN=$2 local PID=$(ps aux | grep "$SEARCH_PATTERN" | grep -v grep | awk '{print $2}' | head -n 1) if [ -z "$PID" ]; then echo "[SKIP] Process '$PROC_NAME' not found." return fi echo "------------------------------------------------------" echo "[INFO] Auditing $PROC_NAME (PID: $PID)" local GLOBAL_ERROR=0 # 1. Detect GHOST Files (DELETED or missing via stat) local GHOST_DATA=$(lsof -p $PID -n | grep -E "DEL|\(stat:" | grep -v "/dev/zero") if [ ! -z "$GHOST_DATA" ]; then echo "[!!! CRITICAL ALERT !!!] Ghost files (deleted but running) found:" echo "$GHOST_DATA" /usr/local/cwp/php71/bin/php /usr/local/cwpsrv/htdocs/resources/admin/include/libs/notifications/cli.php --level="danger" --subject="CWP Security Audit - Ghost files (deleted but running)" --message="[!!! CRITICAL ALERT !!!] Ghost files (deleted but running) found, for more info run: sh /scripts/cwp_security_audit" GLOBAL_ERROR=1 fi # 2. Deep Memory Audit (Path + RPM Package Check) local CURRENT_MEM=$(lsof -p $PID -n | grep "mem" | awk '{for(i=9;i<=NF;i++) printf "%s ", $i; print ""}' | sed 's/(stat:.*//' | xargs) for FILE in $CURRENT_MEM; do [[ -z "$FILE" || "$FILE" == "REG" || "$FILE" == "mem" || "$FILE" == "/" ]] && continue local MATCH=0 for ALLOWED in "${ALLOWED_LIB_PATHS[@]}"; do if [[ "$FILE" == "$ALLOWED"* ]]; then MATCH=1; break; fi done for ALLOWED in "${ALLOWED_BINARIES[@]}"; do if [[ "$FILE" == "$ALLOWED" ]]; then MATCH=1; break; fi done if [ $MATCH -eq 0 ]; then echo "[SECURITY ALERT] Unknown/Untrusted file: $FILE" /usr/local/cwp/php71/bin/php /usr/local/cwpsrv/htdocs/resources/admin/include/libs/notifications/cli.php --level="danger" --subject="CWP Security Audit - Unknown/Untrusted file" --message="[SECURITY ALERT] Unknown/Untrusted file: $FILE" GLOBAL_ERROR=1 else if [[ "$FILE" == "/usr/lib64/"* ]]; then if ! rpm -qf "$FILE" &>/dev/null; then echo "[!!! DANGER !!!] File in system path but NOT owned by any package: $FILE" /usr/local/cwp/php71/bin/php /usr/local/cwpsrv/htdocs/resources/admin/include/libs/notifications/cli.php --level="danger" --subject="CWP Security Audit - File in system path" --message="[!!! DANGER !!!] File in system path but NOT owned by any package, for more info run: sh /scripts/cwp_security_audit" GLOBAL_ERROR=1 fi fi fi done # 3. Port Audit local CURRENT_PORTS=$(lsof -Pan -p $PID -iTCP -sTCP:LISTEN 2>/dev/null | awk 'NR>1 {split($9,a,":"); print a[length(a)]}') for PORT in $CURRENT_PORTS; do local PORT_MATCH=0 for ALLOWED in "${ALLOWED_PORTS[@]}"; do if [ "$PORT" == "$ALLOWED" ]; then PORT_MATCH=1; break; fi done if [ $PORT_MATCH -eq 0 ]; then echo "[SECURITY ALERT] Unauthorized port: $PORT" /usr/local/cwp/php71/bin/php /usr/local/cwpsrv/htdocs/resources/admin/include/libs/notifications/cli.php --level="danger" --subject="CWP Security Audit - Unauthorized port: $PORT" --message="[SECURITY ALERT] Unauthorized port, for more info run: sh /scripts/cwp_security_audit" GLOBAL_ERROR=1 fi done [ $GLOBAL_ERROR -eq 0 ] && echo "[OK] $PROC_NAME looks clean." } # --- EXECUTION --- check_process "cwpsrv" "cwpsrv: master process" check_process "php-fpm-cwp" "php-fpm: master process .*cwpsrv.conf" check_process "apache" "/usr/local/apache/bin/httpd" echo "------------------------------------------------------" echo "[DONE] Security audit finished." Problem: CWP updates may overwrite the fixCWP updates may overwrite /scripts/cwp_security_audit, so manually patching the file once is not always enough.One practical solution is to keep a local fixed copy and automatically restore it if CWP replaces the file during an update.The following installer creates:/root/cwp-overrides/cwp_security_audit.fixed /root/cwp-overrides/repair-cwp-security-audit.sh /etc/systemd/system/cwp-security-audit-override.service /etc/systemd/system/cwp-security-audit-override.path /etc/cron.d/cwp-security-audit-override The systemd.path unit watches /scripts/cwp_security_audit. If the file changes, the repair script compares it to the fixed version and restores the patched file if needed. A daily cron fallback is also added in case the file watch misses an event.Installer scriptSave this as:install-cwp-security-audit-override.sh Then run it as root:chmod +x install-cwp-security-audit-override.sh ./install-cwp-security-audit-override.sh #!/bin/bash set -euo pipefail # ============================================================ # CWP security audit override installer # Restores the locally fixed /scripts/cwp_security_audit # if CWP updates overwrite it. # ============================================================ if [ "$(id -u)" -ne 0 ]; then echo "ERROR: This installer must be run as root." exit 1 fi OVERRIDE_DIR="/root/cwp-overrides" BACKUP_DIR="${OVERRIDE_DIR}/backups" FIXED_FILE="${OVERRIDE_DIR}/cwp_security_audit.fixed" REPAIR_SCRIPT="${OVERRIDE_DIR}/repair-cwp-security-audit.sh" TARGET="/scripts/cwp_security_audit" SERVICE_FILE="/etc/systemd/system/cwp-security-audit-override.service" PATH_FILE="/etc/systemd/system/cwp-security-audit-override.path" CRON_FILE="/etc/cron.d/cwp-security-audit-override" LOG_FILE="/var/log/cwp-security-audit-override.log" echo "------------------------------------------------------" echo "[INFO] Installing CWP security audit override" echo "------------------------------------------------------" mkdir -p "$OVERRIDE_DIR" "$BACKUP_DIR" chmod 700 "$OVERRIDE_DIR" chmod 700 "$BACKUP_DIR" if ! command -v lsof >/dev/null 2>&1; then echo "[INFO] lsof not found. Installing..." if command -v dnf >/dev/null 2>&1; then dnf install -y lsof elif command -v yum >/dev/null 2>&1; then yum install -y lsof else echo "WARNING: Neither dnf nor yum found. Please install lsof manually." fi fi if [ -f "$TARGET" ]; then INITIAL_BACKUP="${BACKUP_DIR}/cwp_security_audit.initial.$(date '+%Y%m%d-%H%M%S').bak" cp -a "$TARGET" "$INITIAL_BACKUP" echo "[INFO] Current target backed up to: $INITIAL_BACKUP" else echo "[WARNING] Target file does not exist yet: $TARGET" fi cat > "$FIXED_FILE" <<'CWP_FIXED_SCRIPT' #!/bin/bash # --- CONFIGURATION --- ALLOWED_LIB_PATHS=( "/usr/lib64/lib" "/usr/lib64/ld-" "/usr/local/ioncube/" "/usr/lib/locale/" "/usr/local/cwp/" "/usr/local/apache/modules/" "/usr/local/lib/" "/usr/lib64/gconv/" ) ALLOWED_BINARIES=( "/usr/local/cwpsrv/bin/cwpsrv" "/usr/local/cwp/php71/sbin/php-fpm" "/usr/local/apache/bin/httpd" ) ALLOWED_PORTS=("2030" "2031" "2082" "2083" "2086" "2087" "2095" "2096" "9000" "2302" "2304" "8181" "8443" "80" "443") if ! command -v lsof &> /dev/null; then yum install -y lsof fi check_process() { local PROC_NAME=$1 local SEARCH_PATTERN=$2 local PID=$(ps aux | grep "$SEARCH_PATTERN" | grep -v grep | awk '{print $2}' | head -n 1) if [ -z "$PID" ]; then echo "[SKIP] Process '$PROC_NAME' not found." return fi echo "------------------------------------------------------" echo "[INFO] Auditing $PROC_NAME (PID: $PID)" local GLOBAL_ERROR=0 local GHOST_DATA=$(lsof -p $PID -n | grep -E "DEL|\(stat:" | grep -v "/dev/zero") if [ ! -z "$GHOST_DATA" ]; then echo "[!!! CRITICAL ALERT !!!] Ghost files (deleted but running) found:" echo "$GHOST_DATA" /usr/local/cwp/php71/bin/php /usr/local/cwpsrv/htdocs/resources/admin/include/libs/notifications/cli.php --level="danger" --subject="CWP Security Audit - Ghost files (deleted but running)" --message="[!!! CRITICAL ALERT !!!] Ghost files (deleted but running) found, for more info run: sh /scripts/cwp_security_audit" GLOBAL_ERROR=1 fi local CURRENT_MEM=$(lsof -p $PID -n | grep "mem" | awk '{for(i=9;i<=NF;i++) printf "%s ", $i; print ""}' | sed 's/(stat:.*//' | xargs) for FILE in $CURRENT_MEM; do [[ -z "$FILE" || "$FILE" == "REG" || "$FILE" == "mem" || "$FILE" == "/" ]] && continue local MATCH=0 for ALLOWED in "${ALLOWED_LIB_PATHS[@]}"; do if [[ "$FILE" == "$ALLOWED"* ]]; then MATCH=1; break; fi done for ALLOWED in "${ALLOWED_BINARIES[@]}"; do if [[ "$FILE" == "$ALLOWED" ]]; then MATCH=1; break; fi done if [ $MATCH -eq 0 ]; then echo "[SECURITY ALERT] Unknown/Untrusted file: $FILE" /usr/local/cwp/php71/bin/php /usr/local/cwpsrv/htdocs/resources/admin/include/libs/notifications/cli.php --level="danger" --subject="CWP Security Audit - Unknown/Untrusted file" --message="[SECURITY ALERT] Unknown/Untrusted file: $FILE" GLOBAL_ERROR=1 else if [[ "$FILE" == "/usr/lib64/"* ]]; then if ! rpm -qf "$FILE" &>/dev/null; then echo "[!!! DANGER !!!] File in system path but NOT owned by any package: $FILE" /usr/local/cwp/php71/bin/php /usr/local/cwpsrv/htdocs/resources/admin/include/libs/notifications/cli.php --level="danger" --subject="CWP Security Audit - File in system path" --message="[!!! DANGER !!!] File in system path but NOT owned by any package, for more info run: sh /scripts/cwp_security_audit" GLOBAL_ERROR=1 fi fi fi done local CURRENT_PORTS=$(lsof -Pan -p $PID -iTCP -sTCP:LISTEN 2>/dev/null | awk 'NR>1 {split($9,a,":"); print a[length(a)]}') for PORT in $CURRENT_PORTS; do local PORT_MATCH=0 for ALLOWED in "${ALLOWED_PORTS[@]}"; do if [ "$PORT" == "$ALLOWED" ]; then PORT_MATCH=1; break; fi done if [ $PORT_MATCH -eq 0 ]; then echo "[SECURITY ALERT] Unauthorized port: $PORT" /usr/local/cwp/php71/bin/php /usr/local/cwpsrv/htdocs/resources/admin/include/libs/notifications/cli.php --level="danger" --subject="CWP Security Audit - Unauthorized port: $PORT" --message="[SECURITY ALERT] Unauthorized port, for more info run: sh /scripts/cwp_security_audit" GLOBAL_ERROR=1 fi done [ $GLOBAL_ERROR -eq 0 ] && echo "[OK] $PROC_NAME looks clean." } check_process "cwpsrv" "cwpsrv: master process" check_process "php-fpm-cwp" "php-fpm: master process .*cwpsrv.conf" check_process "apache" "/usr/local/apache/bin/httpd" echo "------------------------------------------------------" echo "[DONE] Security audit finished." CWP_FIXED_SCRIPT chmod 600 "$FIXED_FILE" cat > "$REPAIR_SCRIPT" <<'REPAIR_SCRIPT' #!/bin/bash set -euo pipefail TARGET="/scripts/cwp_security_audit" FIXED="/root/cwp-overrides/cwp_security_audit.fixed" BACKUP_DIR="/root/cwp-overrides/backups" LOG="/var/log/cwp-security-audit-override.log" mkdir -p "$BACKUP_DIR" timestamp="$(date '+%Y-%m-%d %H:%M:%S')" notify_cwp() { local level="$1" local subject="$2" local message="$3" if [ -x /usr/local/cwp/php71/bin/php ] && [ -f /usr/local/cwpsrv/htdocs/resources/admin/include/libs/notifications/cli.php ]; then /usr/local/cwp/php71/bin/php \ /usr/local/cwpsrv/htdocs/resources/admin/include/libs/notifications/cli.php \ --level="$level" \ --subject="$subject" \ --message="$message" \ >/dev/null 2>&1 || true fi } if [ ! -f "$FIXED" ]; then echo "[$timestamp] ERROR: fixed file not found: $FIXED" >> "$LOG" notify_cwp "danger" \ "CWP override error" \ "Fixed CWP security audit file not found: $FIXED" exit 1 fi if [ ! -f "$TARGET" ]; then echo "[$timestamp] WARNING: target file missing, restoring: $TARGET" >> "$LOG" install -m 755 "$FIXED" "$TARGET" notify_cwp "warning" \ "CWP security audit restored" \ "Target file was missing and has been restored: $TARGET" exit 0 fi target_hash="$(sha256sum "$TARGET" | awk '{print $1}')" fixed_hash="$(sha256sum "$FIXED" | awk '{print $1}')" if [ "$target_hash" != "$fixed_hash" ]; then backup="$BACKUP_DIR/cwp_security_audit.$(date '+%Y%m%d-%H%M%S').bak" cp -a "$TARGET" "$backup" install -m 755 "$FIXED" "$TARGET" echo "[$timestamp] RESTORED: $TARGET was changed. Backup saved to: $backup" >> "$LOG" notify_cwp "warning" \ "CWP override restored cwp_security_audit" \ "CWP update changed /scripts/cwp_security_audit. The local fixed version was restored. Backup: $backup" else echo "[$timestamp] OK: no change detected." >> "$LOG" fi REPAIR_SCRIPT chmod 700 "$REPAIR_SCRIPT" cat > "$SERVICE_FILE" <<'SERVICE_UNIT' [Unit] Description=Restore local fixed CWP security audit script if overwritten [Service] Type=oneshot ExecStart=/root/cwp-overrides/repair-cwp-security-audit.sh SERVICE_UNIT chmod 644 "$SERVICE_FILE" cat > "$PATH_FILE" <<'PATH_UNIT' [Unit] Description=Watch CWP security audit script for changes [Path] PathChanged=/scripts/cwp_security_audit PathModified=/scripts/cwp_security_audit Unit=cwp-security-audit-override.service [Install] WantedBy=multi-user.target PATH_UNIT chmod 644 "$PATH_FILE" cat > "$CRON_FILE" <<'CRON_FALLBACK' # CWP security audit override fallback check # Runs daily in case systemd.path missed a file change. 17 3 * * * root /root/cwp-overrides/repair-cwp-security-audit.sh >/dev/null 2>&1 CRON_FALLBACK chmod 644 "$CRON_FILE" systemctl daemon-reload systemctl enable --now cwp-security-audit-override.path echo "[INFO] Running first repair/check..." "$REPAIR_SCRIPT" echo "------------------------------------------------------" echo "[OK] Installation finished." echo echo "Status:" systemctl --no-pager status cwp-security-audit-override.path || true echo echo "Last log entries:" tail -n 10 "$LOG_FILE" 2>/dev/null || true echo "------------------------------------------------------" VerificationAfter installation, run:systemctl status cwp-security-audit-override.path tail -n 30 /var/log/cwp-security-audit-override.log sha256sum /scripts/cwp_security_audit /root/cwp-overrides/cwp_security_audit.fixed The two sha256sum values should be identical.Then run the CWP audit again:sh /scripts/cwp_security_audit On a clean AlmaLinux 9.x server, the previous false alerts for:/usr/lib64/gconv/gconv-modules.cache and:Unauthorized port: php-fpm should be gone.NotesThis does not disable the CWP security audit. It only fixes two false-positive conditions:missing allowed path for /usr/lib64/gconv/,unsafe parsing of listening ports from generic lsof output.The script also keeps backups of any CWP-provided version that gets overwritten, so you can later compare what changed after an update:ls -lah /root/cwp-overrides/backups/ This approach is safer than using:chattr +i /scripts/cwp_security_audit because CWP updates are not blocked. The update can complete normally, and the local fixed version is restored afterwards.
Create an account or sign in to comment