Server : Apache System : Linux webd003.cluster111.gra.hosting.ovh.net 5.15.206-ovh-vps-grsec-zfs-classid #1 SMP Fri May 15 02:41:25 UTC 2026 x86_64 User : edevmultrx ( 811899) PHP Version : 8.1.33 Disable Function : _dyuweyrj4,_dyuweyrj4r,dl Directory : /home/edevmultrx/ |
#!/bin/bash
# Watchdog Filester — OVH cron compatible
# Usage:
# ./watchdog_filester.sh # report-only
# ./watchdog_filester.sh --auto-clean # auto-quarantine if found
set -u
TS=$(date -u +%Y%m%d-%H%M%S)
LOG_DIR="/home/edevmultrx/watchdog-logs"
mkdir -p "$LOG_DIR"
LOG="$LOG_DIR/watchdog_${TS}.log"
QDIR="/home/edevmultrx/.quarantine-watchdog-${TS}"
AUTO_CLEAN=0
[ "${1:-}" = "--auto-clean" ] && AUTO_CLEAN=1
exec > "$LOG" 2>&1
WARN=0
echo "=== Filester Watchdog $(date -u) ==="
echo "Mode: $([ $AUTO_CLEAN -eq 1 ] && echo 'AUTO-CLEAN' || echo 'REPORT-ONLY')"
echo ""
# 1) Filester/fileorganizer plugin folders in production (excl. quarantine)
echo "--- 1) Filester/fileorganizer in production ---"
FOUND=$(find /home/edevmultrx/www -type d \( -name "filester" -o -name "fileorganizer" -o -name "ninja-file-manager" \) 2>/dev/null)
if [ -n "$FOUND" ]; then
echo "ALERT FOUND:"
echo "$FOUND"
WARN=$((WARN+1))
if [ $AUTO_CLEAN -eq 1 ]; then
mkdir -p "$QDIR"
while IFS= read -r dir; do
tgt="$QDIR/$(echo "$dir" | tr '/' '_')"
chmod -R u+w "$dir" 2>/dev/null
mv "$dir" "$tgt" && echo " QUARANTINED -> $tgt"
done <<< "$FOUND"
fi
else
echo "OK No filester/fileorganizer in production"
fi
echo ""
# 2) DB active_plugins for filester/fileorganizer
echo "--- 2) active_plugins (BDD) ---"
INFECTED=0
for cfg in /home/edevmultrx/www/*/wp-config.php /home/edevmultrx/www/*/*/wp-config.php; do
[ -f "$cfg" ] || continue
site=$(basename "$(dirname "$cfg")")
db_pass=$(grep DB_PASSWORD "$cfg" | head -1 | sed -E "s/.*['\"]([^'\"]+)['\"].*/\1/")
db_name=$(grep DB_NAME "$cfg" | head -1 | sed -E "s/.*['\"]([^'\"]+)['\"].*/\1/")
db_user=$(grep DB_USER "$cfg" | head -1 | sed -E "s/.*['\"]([^'\"]+)['\"].*/\1/")
db_host=$(grep DB_HOST "$cfg" | head -1 | sed -E "s/.*['\"]([^'\"]+)['\"].*/\1/")
prefix=$(grep -E '\$table_prefix' "$cfg" | head -1 | sed -E "s/.*['\"]([^'\"]+)['\"];.*/\1/")
[ -z "$db_name" ] && continue
match=$(MYSQL_PWD="$db_pass" mysql --skip-column-names -B -h "$db_host" -u "$db_user" "$db_name" -e "SELECT 1 FROM ${prefix}options WHERE option_name='active_plugins' AND (option_value LIKE '%filester/%' OR option_value LIKE '%fileorganizer/%') LIMIT 1;" 2>/dev/null)
if [ -n "$match" ]; then
echo "ALERT $site (prefix $prefix) has filester/fileorganizer active in DB"
INFECTED=$((INFECTED+1))
if [ $AUTO_CLEAN -eq 1 ]; then
/usr/local/php8.1/bin/php -r '
$conn = new mysqli($argv[1], $argv[2], $argv[3], $argv[4]);
$res = $conn->query("SELECT option_value FROM {$argv[5]}options WHERE option_name=\"active_plugins\"");
$row = $res->fetch_assoc();
$arr = unserialize($row["option_value"]);
$filtered = array();
foreach ($arr as $p) {
if (strpos($p, "filester/") === false && strpos($p, "fileorganizer/") === false) $filtered[] = $p;
}
$new = serialize(array_values($filtered));
$stmt = $conn->prepare("UPDATE {$argv[5]}options SET option_value=? WHERE option_name=\"active_plugins\"");
$stmt->bind_param("s", $new);
$stmt->execute();
echo " CLEANED active_plugins\n";
' "$db_host" "$db_user" "$db_pass" "$db_name" "$prefix"
fi
fi
done
if [ $INFECTED -eq 0 ]; then
echo "OK No filester/fileorganizer in active_plugins"
else
WARN=$((WARN+1))
fi
echo ""
# 3) Webshells at site ROOT only (NOT wp-admin which is WP core)
echo "--- 3) Webshells at site ROOT (excludes wp-admin/) ---"
# jp.* always malicious wherever it is
jp_found=$(find /home/edevmultrx/www -maxdepth 4 \( -name "jp.php" -o -name "jp.pHtMl" \) 2>/dev/null)
# typo-squat backdoors always malicious
typo_found=$(find /home/edevmultrx/www -maxdepth 4 \( -name "wp-loiin.php" -o -name "wp-crrm.php" -o -name "wp-haader.php" -o -name "wp-conffg.php" -o -name "wper.php" -o -name "wp-sx-generator.php" \) 2>/dev/null)
# admin.php only suspicious if at site ROOT (mindepth=2 maxdepth=2 = directly in /home/edevmultrx/www/<site>/)
root_admin=$(find /home/edevmultrx/www -mindepth 2 -maxdepth 2 -name "admin.php" 2>/dev/null)
# Combine
all_webshells="$jp_found"
[ -n "$typo_found" ] && all_webshells="$all_webshells${all_webshells:+$'\n'}$typo_found"
[ -n "$root_admin" ] && all_webshells="$all_webshells${all_webshells:+$'\n'}$root_admin"
if [ -n "$all_webshells" ]; then
echo "ALERT WEBSHELLS:"
echo "$all_webshells"
WARN=$((WARN+1))
else
echo "OK No webshells at site root level"
fi
echo ""
echo "=== Warnings: $WARN ==="
# Rotate logs - keep last 30
ls -t "$LOG_DIR"/watchdog_*.log 2>/dev/null | tail -n +31 | xargs -r rm 2>/dev/null
exit $WARN