| Server IP : 156.238.232.47 / Your IP : 216.73.216.150 Web Server : nginx/1.25.3 System : Linux C202504152095410 3.10.0-1160.119.1.el7.x86_64 #1 SMP Tue Jun 4 14:43:51 UTC 2024 x86_64 User : www ( 1000) PHP Version : 8.3.25 Disable Function : passthru,exec,system,putenv,chroot,chgrp,chown,shell_exec,popen,proc_open,pcntl_exec,ini_alter,ini_restore,dl,openlog,syslog,readlink,symlink,popepassthru,pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wifcontinued,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority,imap_open,apache_setenv MySQL : OFF | cURL : ON | WGET : ON | Perl : ON | Python : ON | Sudo : ON | Pkexec : ON Directory : /www/ |
Upload File : |
#!/usr/bin/env bash
set -u
DEFAULT_ROOT="/www/wwwroot"
CLEAN=0
ROOT="$DEFAULT_ROOT"
usage() {
cat <<'EOF'
用法:
bash scan_eyoucms_backdoor.sh
bash scan_eyoucms_backdoor.sh /www/wwwroot
bash scan_eyoucms_backdoor.sh /www/wwwroot --clean
说明:
- 默认只扫描,不修改任何文件。
- 清理模式需要输入精确小写 yes 才会执行。
- 清理模式会直接删除已确认的 core/start.php 后门代码和落地文件,不做备份。
EOF
}
for arg in "$@"; do
case "$arg" in
--clean)
CLEAN=1
;;
-h|--help)
usage
exit 0
;;
*)
ROOT="$arg"
;;
esac
done
if [ ! -d "$ROOT" ]; then
echo "[错误] 根目录不存在: $ROOT"
exit 2
fi
is_eyoucms_site() {
local site="$1"
[ -f "$site/index.php" ] &&
[ -f "$site/login.php" ] &&
[ -f "$site/core/start.php" ] &&
[ -d "$site/application" ] &&
[ -d "$site/data" ]
}
add_site_if_eyoucms() {
local candidate="$1"
if is_eyoucms_site "$candidate"; then
SITES+=("$candidate")
fi
}
scan_site() {
local site="$1"
local start_file="$site/core/start.php"
local hit_count=0
local landing_count=0
echo "------------------------------------------------------------"
echo "[站点] $site"
if [ ! -f "$start_file" ]; then
echo "[跳过] 缺少 core/start.php"
return 0
fi
local patterns=(
'function_tool[[:space:]]*\([[:space:]]*\)[[:space:]]*;'
'function[[:space:]]+function_tool[[:space:]]*\('
'str_rot13'
'ppbbccllgrzc'
'AAPPIv3\.php'
'jsc-cdn'
'n11939n301p3nr15018qs'
)
for pattern in "${patterns[@]}"; do
if grep -Eq "$pattern" "$start_file"; then
echo "[命中] core/start.php 特征: $pattern"
hit_count=$((hit_count + 1))
fi
done
local payloads=(
"$site/caches/tpl/a11939a301c3ae15018df9a8a2b102pp"
"$site/themes/n11939n301p3nr15018qs559n8n2o102cf"
)
for payload in "${payloads[@]}"; do
if [ -e "$payload" ]; then
echo "[命中] 恶意落地文件: $payload"
landing_count=$((landing_count + 1))
fi
done
if [ "$hit_count" -gt 0 ] || [ "$landing_count" -gt 0 ]; then
echo "[结果] 感染或可疑 特征命中=$hit_count 落地文件=$landing_count"
INFECTED_SITES+=("$site")
else
echo "[结果] 干净:未发现已知 function_tool 后门特征"
fi
}
remove_function_tool_from_start() {
local start_file="$1"
local py_bin=""
for cmd in python3 python python2; do
if command -v "$cmd" >/dev/null 2>&1; then
py_bin="$cmd"
break
fi
done
if [ -z "$py_bin" ]; then
echo "[错误] 未找到 python3/python/python2,无法安全编辑 $start_file"
return 1
fi
"$py_bin" - "$start_file" <<'PY'
import re
import sys
path = sys.argv[1]
with open(path, "rb") as fh:
data = fh.read()
data = re.sub(
b"(?m)^[ \\t]*function_tool\\s*\\(\\s*\\)\\s*;\\s*(?=//[^\\n]*\\n)",
b"",
data,
count=1,
)
data = re.sub(
b"(?m)^[ \\t]*function_tool\\s*\\(\\s*\\)\\s*;\\s*\\n?",
b"",
data,
count=1,
)
needle = b"function function_tool"
pos = data.find(needle)
if pos >= 0:
brace = data.find(b"{", pos)
if brace >= 0:
i = brace
depth = 0
state = "code"
escape = False
end = -1
length = len(data)
while i < length:
ch = data[i:i + 1]
nxt = data[i + 1:i + 2] if i + 1 < length else b""
if state == "line_comment":
if ch == b"\n":
state = "code"
elif state == "block_comment":
if ch == b"*" and nxt == b"/":
state = "code"
i += 1
elif state == "single":
if escape:
escape = False
elif ch == b"\\":
escape = True
elif ch == b"'":
state = "code"
elif state == "double":
if escape:
escape = False
elif ch == b"\\":
escape = True
elif ch == b'"':
state = "code"
else:
if ch == b"/" and nxt == b"/":
state = "line_comment"
i += 1
elif ch == b"#":
state = "line_comment"
elif ch == b"/" and nxt == b"*":
state = "block_comment"
i += 1
elif ch == b"'":
state = "single"
elif ch == b'"':
state = "double"
elif ch == b"{":
depth += 1
elif ch == b"}":
depth -= 1
if depth == 0:
end = i + 1
while end < length and data[end:end + 1] in b" \t\r\n":
end += 1
break
i += 1
if end > pos:
data = data[:pos] + data[end:]
with open(path, "wb") as fh:
fh.write(data)
PY
}
clean_site() {
local site="$1"
local start_file="$site/core/start.php"
echo "------------------------------------------------------------"
echo "[清理] $site"
if [ -f "$start_file" ]; then
if ! remove_function_tool_from_start "$start_file"; then
echo "[错误] 清理 core/start.php 失败: $start_file"
exit 5
fi
echo "[清理] 已从 core/start.php 移除 function_tool 调用和函数体(如存在)"
fi
local payloads=(
"$site/caches/tpl/a11939a301c3ae15018df9a8a2b102pp"
"$site/themes/n11939n301p3nr15018qs559n8n2o102cf"
)
for payload in "${payloads[@]}"; do
if [ -e "$payload" ]; then
rm -f -- "$payload"
echo "[清理] 已删除恶意落地文件: $payload"
fi
done
if [ -d "$site/core" ]; then
if chmod -R 555 "$site/core"; then
echo "[加固] 已将 core 目录递归设置为 555 权限: $site/core"
else
echo "[错误] 设置 core 目录 555 权限失败: $site/core"
exit 6
fi
fi
}
SITES=()
INFECTED_SITES=()
add_site_if_eyoucms "$ROOT"
while IFS= read -r -d '' child; do
add_site_if_eyoucms "$child"
done < <(find "$ROOT" -mindepth 1 -maxdepth 1 -type d -print0 2>/dev/null)
if [ "${#SITES[@]}" -eq 0 ]; then
echo "[信息] 未在该目录下发现疑似 EyouCMS 站点: $ROOT"
exit 0
fi
echo "[信息] 发现疑似 EyouCMS 站点数量: ${#SITES[@]}"
for site in "${SITES[@]}"; do
scan_site "$site"
done
echo "============================================================"
echo "[汇总] 感染或可疑站点=${#INFECTED_SITES[@]} 已扫描站点=${#SITES[@]}"
if [ "${#INFECTED_SITES[@]}" -eq 0 ]; then
exit 0
fi
if [ "$CLEAN" -ne 1 ]; then
echo "[信息] 当前仅扫描,不会清理。需要清理请加 --clean 参数运行。"
exit 1
fi
echo "[警告] 清理模式会直接修改/删除文件,不做备份。"
echo "[警告] 将处理以下站点:"
for site in "${INFECTED_SITES[@]}"; do
echo " - $site"
done
printf "请输入精确小写 yes 以清理所有命中站点: "
read -r answer
if [ "$answer" != "yes" ]; then
echo "[中止] 输入不是 yes,未修改任何文件。"
exit 3
fi
for site in "${INFECTED_SITES[@]}"; do
clean_site "$site"
done
echo "============================================================"
echo "[信息] 清理后复扫"
INFECTED_SITES=()
for site in "${SITES[@]}"; do
scan_site "$site"
done
echo "============================================================"
echo "[汇总] 剩余感染或可疑站点=${#INFECTED_SITES[@]} 已扫描站点=${#SITES[@]}"
if [ "${#INFECTED_SITES[@]}" -gt 0 ]; then
exit 4
fi
exit 0