今天上午看到论坛有人发哪吒面板被入侵的帖子,回头一查自己也中了。折腾了一整天把十几台机器清了一遍,把过程和自查脚本整理出来,希望能帮到同样中招的兄弟。
一、事件概况
- 入口:哪吒面板(Dashboard)漏洞。攻击者先攻破主控,再通过主控的"远程执行命令"功能,向所有挂在上面的 Agent 节点批量下发 payload。
- 性质:自动化批量肉鸡攻击(图挖矿/代理变现),不是定向 APT。一晚上批量铺开,时间戳高度一致。
- 关键点:攻击者全程没走 SSH。我 SSH 早就只留密钥、禁了密码登录,但这道防线根本没用上——因为 Agent 是以 root 跑的,主控让它执行啥就执行啥,不需要 SSH。所以"我关了密码登录为什么还中招"的答案是:它走的是另一扇门。
二、攻击者植入了什么(IOC 清单)
- 恶意哪吒 Agent:在 /opt/nezha/agent/ 下生成随机名 config-xxxxx.yml,里面的主控地址指向攻击者的 C2(不是你自己的主控)。
- SSH 后门公钥:往 authorized_keys 塞一把带特定注释的公钥做持久化。
- XMRig 挖矿:/root/c3pool/ 目录 + c3pool_miner.service 自启,连 c3pool 矿池。
- 守护/复活服务:systemlog.service + /opt/systemlog/,用来在你杀掉后门后把它重新拉起来。
- memfd 内存马:最隐蔽的一个。用 memfd_create 把程序只放内存、磁盘无文件,还伪装成 [kworker/x:x] 内核线程。普通的查文件/查cron/查service 全扫不到它。
三、怎么发现 memfd 内存马的(重点)
内存马磁盘上没文件,ls、cron、systemctl 都查不出。它的破绽在进程:
- ps 能看到它(再隐蔽也得有进程在跑)
- /proc/PID/exe 会指向
/memfd:xxx (deleted)—— 一看到 memfd 就是铁证 - 它伪装成 [kworker/0:2],但真 kworker 是内核线程、父进程是 PID 2、且没有 /proc/PID/exe;伪装的则有真实 exe
内存马怕重启、怕 kill(只在内存里),但要确认有没有"守护服务"会把它复活——所以杀完要反复查它有没有以新 PID 复活。
四、处理流程(供参考)
- 堵入口:彻底删哪吒(stop+disable+rm /opt/nezha),这是根因,不删就还会被同样手法打。监控可以换 komari 之类没有命令执行能力的。
- 清植入物:逐台杀恶意 agent / 内存马、删后门公钥、删挖矿和守护服务、封 C2。
- 换锁:所有 SSH key 当泄露处理,重新生成、每台一把、清空重建 authorized_keys、禁用密码登录。
- 轮换凭据:被 root 控制过,理论上能读到的高价值凭据都换一遍(邮箱、CF、域名、面板等),密码库加 2FA。
- 取证复盘:查登录记录(last)、认证日志、账号(有没有影子 root)、文件改动时间线、cron、面板日志,确认有没有更深的痕迹。
- 重装:被 root 控制过的机器,洁癖标准是重装(清理只是止血,无法 100% 保证没有更深的持久化)。可按优先级慢慢滚动重装。
五、自查脚本(只读,不删东西)
下面这个脚本把上面 9 类植入物的检测打包了,纯只读、只报警、不删改任何东西,可以放心跑。发现 [警] 的项再人工核查、手动清理。
用法:
- 单机:
bash nezha_ioc_check.sh - 批量:
ssh 你的节点 'bash -s' < nezha_ioc_check.sh
⚠️ 两个地方记得改成你自己的环境,避免误报:
- 第 2 项的 EXCLUDE_COMM:加上你自己合法的、恰好以 k 开头的程序名(我用 komari 监控就加了 komari)
- 第 3 项的路径白名单:把你自己正常程序所在目录(脚本里是 /app)加进去
(脚本见下方附件 / 代码块)
六、一点教训
- 哪吒这种带"远程命令执行"的面板,主控一旦失守,所有 Agent 节点全是 root 沦陷,杀伤力极大。面板/后台一定要收口(只监听本地 + SSH 隧道访问,别裸奔公网)。
- "我只开了密钥登录"挡不住这种攻击,因为它不走 SSH。安全要看攻击的实际路径,别想当然。
- 及时更新面板版本。我就是一直停在旧版本才中招的。
有补充的欢迎跟帖,一起完善。
#!/bin/bash
# ============================================================
# 哪吒面板入侵 自查脚本 (nezha_ioc_check.sh)
# ------------------------------------------------------------
# 用途:排查 2026-06 哪吒面板漏洞批量入侵的常见植入物
# 特点:只读检测,不删除/不修改任何东西,可放心运行
# 用法:直接在被检查的服务器上执行 bash nezha_ioc_check.sh
# 或从本地批量: ssh 节点 'bash -s' < nezha_ioc_check.sh
# ------------------------------------------------------------
# 发现 [警] 即需人工核查;全部显示"未发现"则该项干净。
# 注意:本脚本只负责"发现",清理请人工判断后手动进行。
# ============================================================
ALERT=0
echo "=========================================="
echo " 哪吒入侵自查: $(hostname) $(date '+%F %T')"
echo "=========================================="
# ---- 1) memfd 内存马 ----
# 攻击者用 memfd_create 把恶意程序只放在内存、磁盘无文件,
# 常伪装成 [kworker/x:x]。靠 /proc/PID/exe 指向 memfd 识别。
echo "[1] memfd 内存马"
for pid in $(ls /proc 2>/dev/null | grep -E '^[0-9]+$'); do
if ls -l /proc/$pid/exe 2>/dev/null | grep -qi "memfd"; then
echo " [警] PID $pid 指向 memfd cmd=$(cat /proc/$pid/cmdline 2>/dev/null | tr '\0' ' ')"
ALERT=1
fi
done
# ---- 2) kworker 伪装(进程名像内核线程,却有用户态 exe)----
# 真内核线程父进程是 kthreadd(PID 2)且无 exe;伪装的则有真实 exe。
# 注:请把下面 EXCLUDE 里换成你自己合法的、恰好以 k 开头的程序名(如 komari)。
EXCLUDE_COMM="kdump|komari|kubelet"
echo "[2] kworker 伪装进程"
for pid in $(ls /proc 2>/dev/null | grep -E '^[0-9]+$'); do
comm=$(cat /proc/$pid/comm 2>/dev/null)
exe=$(readlink /proc/$pid/exe 2>/dev/null)
ppid=$(awk '{print $4}' /proc/$pid/stat 2>/dev/null)
case "$comm" in
k*)
if [ -n "$exe" ] && [ "$ppid" != "2" ]; then
if ! echo "$comm" | grep -qE "^($EXCLUDE_COMM)" && [ "${exe#*/usr/lib/systemd/}" = "$exe" ]; then
echo " [警] PID $pid 进程名=$comm 父=$ppid exe=$exe"
ALERT=1
fi
fi
;;
esac
done
# ---- 3) 执行已删除文件的进程((deleted))----
# 程序跑起来后删掉自身文件,只留内存副本。排除正常软件路径与升级残留。
# 注:把 /app 换成你自己正常程序所在目录,避免误报。
echo "[3] 已删除文件执行"
for pid in $(ls /proc 2>/dev/null | grep -E '^[0-9]+$'); do
exe=$(readlink /proc/$pid/exe 2>/dev/null)
case "$exe" in
*"(deleted)"*)
case "$exe" in
*/usr/*|*/bin/*|*/sbin/*|*/app/*|*/snap/*) ;;
*) echo " [警] PID $pid exe=$exe"; ALERT=1 ;;
esac
;;
esac
done
# ---- 4) 恶意哪吒 Agent(随机后缀 config / service,连第三方主控)----
echo "[4] 恶意哪吒 Agent 残留"
ps aux | grep -i 'nezha-agent' | grep -v grep | grep -E 'config-[a-z0-9]+\.yml' \
&& { echo " [警] 发现随机后缀 config 的 agent 进程"; ALERT=1; }
ls /etc/systemd/system/ 2>/dev/null | grep -E 'nezha-agent-[a-z0-9]+\.service' \
&& { echo " [警] 发现随机后缀 nezha service"; ALERT=1; }
ls /opt/nezha/agent/config-*.yml 2>/dev/null \
&& { echo " [警] 发现随机 config 文件"; ALERT=1; }
# ---- 5) 挖矿程序(XMRig / c3pool)----
echo "[5] 挖矿程序"
[ -e /root/c3pool ] && { echo " [警] /root/c3pool 目录存在"; ALERT=1; }
pgrep -x xmrig >/dev/null 2>&1 && { echo " [警] xmrig 进程在运行"; ALERT=1; }
[ -e /etc/systemd/system/c3pool_miner.service ] && { echo " [警] c3pool_miner.service 存在"; ALERT=1; }
# ---- 6) 守护/复活服务(SystemLoger / systemlog.service)----
echo "[6] 守护复活服务"
pgrep -x SystemLoger >/dev/null 2>&1 && { echo " [警] SystemLoger 进程在运行"; ALERT=1; }
[ -e /opt/systemlog ] && { echo " [警] /opt/systemlog 目录存在"; ALERT=1; }
[ -e /etc/systemd/system/systemlog.service ] && { echo " [警] systemlog.service 存在"; ALERT=1; }
# ---- 7) SSH 后门公钥 ----
# 网传后门公钥常带 gary 之类注释;这里同时提示你核对公钥总数。
echo "[7] SSH 后门公钥"
grep -i "gary" ~/.ssh/authorized_keys 2>/dev/null \
&& { echo " [警] authorized_keys 含可疑公钥(gary)"; ALERT=1; }
echo " (当前 authorized_keys 公钥数: $(grep -c '^ssh-' ~/.ssh/authorized_keys 2>/dev/null))"
# ---- 8) 自启动持久化(cron / 可疑 service)----
echo "[8] 持久化(cron)"
for u in $(cut -f1 -d: /etc/passwd); do
c=$(crontab -l -u "$u" 2>/dev/null | grep -vE '^\s*#|^\s*$')
[ -n "$c" ] && { echo " [信息] 用户 $u 有 cron(请核对):"; echo "$c" | sed 's/^/ /'; }
done
grep -rEl 'curl|wget|/tmp/|base64 -d' /etc/cron* /var/spool/cron 2>/dev/null \
&& { echo " [警] 上述 cron 文件含可疑下载/执行"; ALERT=1; }
# ---- 9) ld.so.preload 劫持 ----
echo "[9] ld.so.preload"
[ -f /etc/ld.so.preload ] && { echo " [警] /etc/ld.so.preload 存在(默认不该有):"; cat /etc/ld.so.preload | sed 's/^/ /'; ALERT=1; }
# ---- 结论 ----
echo "=========================================="
if [ "$ALERT" -eq 0 ]; then
echo " 结论: 未发现已知植入物 ✅ (但不代表绝对安全,被 root 控制过仍建议重装)"
else
echo " 结论: 发现 [警] 项,请逐条人工核查 ⚠️"
fi
echo "=========================================="
@treasureu #0 我让ai优化了一下脚本,已经解决#!/usr/bin/env bash
============================================================
哪吒面板入侵 IOC 自查脚本 v2
------------------------------------------------------------
用途:
排查哪吒 Dashboard / Agent 被利用后常见植入物:
- memfd 内存马
- 伪装 kworker / 内核线程进程
- 执行已删除文件的进程
- 恶意 nezha-agent / 随机 config
- XMRig / c3pool 挖矿
- systemlog / SystemLoger 守护复活服务
- SSH authorized_keys 后门
- cron / systemd timer / service 持久化
- ld.so.preload 劫持
- 可疑网络连接
- 近期高风险路径文件变动
特点:
只读检测;不会删除、停止、修改任何东西。
用法:
bash nezha_ioc_check_v2.sh
批量:
ssh root@节点 'bash -s' < nezha_ioc_check_v2.sh
可选环境变量:
TRUSTED_NEZHA_ADDR_REGEX='你的面板域名|你的面板IP'
EXCLUDE_COMM_REGEX='^(kdump|komari|kubelet)$'
ALLOW_DELETED_EXE_REGEX='^(/usr/|/bin/|/sbin/|/lib/|/lib64/|/snap/|/app/)'
SINCE_DAYS=14
退出码:
0 = 未发现已知高危 IOC
1 = 有 WARN 项,需要人工复核
2 = 有 ALERT 项,应按已沦陷处理
============================================================
set -u
export LC_ALL=C
ALERT_COUNT=0
WARN_COUNT=0
INFO_COUNT=0
HOSTNAME_NOW="$(hostname 2>/dev/null || echo unknown)"
DATE_NOW="$(date '+%F %T %z' 2>/dev/null || date)"
IS_ROOT=0
[ "$(id -u 2>/dev/null)" = "0" ] && IS_ROOT=1
TRUSTED_NEZHA_ADDR_REGEX="${TRUSTED_NEZHA_ADDR_REGEX:-}"
EXCLUDE_COMM_REGEX="${EXCLUDE_COMM_REGEX:-^(kdump|komari|kubelet)$}"
ALLOW_DELETED_EXE_REGEX="${ALLOW_DELETED_EXE_REGEX:-^(/usr/|/bin/|/sbin/|/lib/|/lib64/|/snap/|/var/lib/docker/|/var/lib/containerd/|/app/)}"
SINCE_DAYS="${SINCE_DAYS:-14}"
print_line() {
printf '%s\n' "------------------------------------------------------------"
}
section() {
printf '\n[%s] %s\n' "$1" "$2"
print_line
}
info() {
INFO_COUNT=$((INFO_COUNT + 1))
printf ' [信息] %s\n' "$*"
}
warn() {
WARN_COUNT=$((WARN_COUNT + 1))
printf ' [警] %s\n' "$*"
}
alert() {
ALERT_COUNT=$((ALERT_COUNT + 1))
printf ' [高危] %s\n' "$*"
}
ok() {
printf ' [未发现] %s\n' "$*"
}
have_cmd() {
command -v "$1" >/dev/null 2>&1
}
safe_cat() {
cat "$1" 2>/dev/null
}
safe_readlink() {
readlink "$1" 2>/dev/null
}
proc_pids() {
find /proc -maxdepth 1 -type d -regex '/proc/[0-9]+' 2>/dev/null | sed 's#/proc/##' | sort -n
}
get_cmdline() {
tr '\0' ' ' < "/proc/$1/cmdline" 2>/dev/null | sed 's/[[:space:]]*$//'
}
get_comm() {
safe_cat "/proc/$1/comm" | head -n 1
}
get_ppid() {
awk '{print $4}' "/proc/$1/stat" 2>/dev/null
}
get_user_by_uid() {
awk -F: -v uid="$1" '$3 == uid {print $1; exit}' /etc/passwd 2>/dev/null
}
get_uid_of_pid() {
awk '/^Uid:/ {print $2; exit}' "/proc/$1/status" 2>/dev/null
}
is_kernel_thread_like_name() {
case "$1" in
kworker*|ksoftirqd*|kthreadd|migration*|watchdog*|rcu_|rcuos|rcuop*|cpuhp*|idle_inject*|kauditd|kswapd*|oom_reaper|writeback|kintegrityd|kblockd|ata_sff|md|edac-poller)
return 0
;;
*)
return 1
;;
esac
}
is_probably_real_kernel_thread() {
pid="$1"
exe="$(safe_readlink "/proc/$pid/exe")"
ppid="$(get_ppid "$pid")"
if [ -z "$exe" ] && [ "$ppid" = "2" ]; then
return 0
fi
return 1
}
print_file_preview() {
file="$1"
max_lines="${2:-20}"
if [ -f "$file" ]; then
sed -n "1,${max_lines}p" "$file" 2>/dev/null | sed 's/^/ /'
fi
}
echo "============================================================"
echo " 哪吒入侵 IOC 自查 v2"
echo " 主机: $HOSTNAME_NOW"
echo " 时间: $DATE_NOW"
echo " 用户: $(id 2>/dev/null || echo unknown)"
echo "============================================================"
if [ "$IS_ROOT" -ne 1 ]; then
warn "当前不是 root,部分 /proc、其他用户 authorized_keys、systemd 信息可能读不到;建议用 root 跑一次。"
fi
section "1" "memfd 内存马 / 无文件执行体"
found_memfd=0
for pid in $(proc_pids); do
exe="$(safe_readlink "/proc/$pid/exe")"
cmd="$(get_cmdline "$pid")"
comm="$(get_comm "$pid")"
uid="$(get_uid_of_pid "$pid")"
user="$(get_user_by_uid "$uid")"
[ -z "$user" ] && user="$uid"
if printf '%s' "$exe" | grep -qiE 'memfd:|/memfd:'; then
alert "PID=$pid USER=$user COMM=$comm EXE=$exe CMD=${cmd:-<empty>}"
found_memfd=1
fi
if [ -d "/proc/$pid/fd" ]; then
memfd_fds="$(find "/proc/$pid/fd" -maxdepth 1 -type l -lname 'memfd:' 2>/dev/null | head -n 5)"
if [ -n "$memfd_fds" ]; then
warn "PID=$pid USER=$user COMM=$comm 存在 memfd fd,需核查 CMD=${cmd:-<empty>}"
printf '%s\n' "$memfd_fds" | sed 's/^/ /'
found_memfd=1
fi
fi
done
[ "$found_memfd" -eq 0 ] && ok "未发现 /proc/PID/exe 或 fd 指向 memfd 的进程"
section "2" "伪装内核线程 / kworker 类进程"
found_fake_kernel=0
for pid in $(proc_pids); do
comm="$(get_comm "$pid")"
[ -z "$comm" ] && continue
if is_kernel_thread_like_name "$comm"; then
if is_probably_real_kernel_thread "$pid"; then
continue
fi
fi
done
[ "$found_fake_kernel" -eq 0 ] && ok "未发现明显伪装 kworker / 内核线程的用户态进程"
section "3" "执行已删除文件的进程"
found_deleted_exe=0
for pid in $(proc_pids); do
exe="$(safe_readlink "/proc/$pid/exe")"
case "$exe" in
"(deleted)")
cmd="$(get_cmdline "$pid")"
comm="$(get_comm "$pid")"
uid="$(get_uid_of_pid "$pid")"
user="$(get_user_by_uid "$uid")"
[ -z "$user" ] && user="$uid"
esac
done
[ "$found_deleted_exe" -eq 0 ] && ok "未发现 exe 指向 deleted 文件的进程"
section "4" "哪吒 Agent / Dashboard 残留与异常配置"
found_nezha=0
if pgrep -af 'nezha|dashboard|agent' >/tmp/nezha_ioc_ps.$$ 2>/dev/null; then
info "发现包含 nezha/dashboard/agent 字样的进程:"
sed 's/^/ /' /tmp/nezha_ioc_ps.$$
found_nezha=1
if grep -Ei 'config-[a-z0-9]+.ya?ml|nezha-agent-[a-z0-9]+|/opt/nezha/agent/config-' /tmp/nezha_ioc_ps.$$ >/dev/null 2>&1; then
alert "发现疑似随机后缀 nezha-agent / config 进程"
fi
fi
rm -f /tmp/nezha_ioc_ps.$$ 2>/dev/null
if [ -d /opt/nezha ]; then
warn "/opt/nezha 目录存在;若已确认中招,建议人工核查后停用并重装相关组件"
found_nezha=1
fi
if [ -d /opt/nezha/agent ]; then
info "/opt/nezha/agent 内容:"
find /opt/nezha/agent -maxdepth 2 -mindepth 1 2>/dev/null | sed 's/^/ /' | head -n 80
fi
if find /opt/nezha/agent -maxdepth 2 -type f ( -name 'config-.yml' -o -name 'config-.yaml' ) 2>/dev/null | grep . >/tmp/nezha_ioc_configs.$$; then
alert "发现随机后缀哪吒 agent config:"
sed 's/^/ /' /tmp/nezha_ioc_configs.$$
found_nezha=1
fi
rm -f /tmp/nezha_ioc_configs.$$ 2>/dev/null
if find /etc/systemd/system /lib/systemd/system /usr/lib/systemd/system
-maxdepth 1 -type f ( -name 'nezha.service' -o -name 'nezha-agent-*.service' )
2>/dev/null | grep . >/tmp/nezha_ioc_units.$$; then
warn "发现哪吒相关 systemd service:"
sed 's/^/ /' /tmp/nezha_ioc_units.$$
found_nezha=1
while IFS= read -r unit; do
[ -f "$unit" ] || continue
if grep -Eiq 'config-[a-z0-9]+.ya?ml|nezha-agent-[a-z0-9]+' "$unit"; then
alert "service 内含随机后缀配置或 agent 名称:$unit"
print_file_preview "$unit" 30
fi
done < /tmp/nezha_ioc_units.$$
fi
rm -f /tmp/nezha_ioc_units.$$ 2>/dev/null
if find /opt/nezha /etc/systemd/system /lib/systemd/system /usr/lib/systemd/system
-type f 2>/dev/null | xargs grep -IEl 'nz_client_secret|nezha|dashboard|grpc|tls' 2>/dev/null | grep . >/tmp/nezha_ioc_grep.$$; then
info "发现哪吒相关配置文件线索:"
sed 's/^/ /' /tmp/nezha_ioc_grep.$$ | head -n 80
fi
rm -f /tmp/nezha_ioc_grep.$$ 2>/dev/null
if [ -n "$TRUSTED_NEZHA_ADDR_REGEX" ]; then
if find /opt/nezha /etc/systemd/system /lib/systemd/system /usr/lib/systemd/system
-type f 2>/dev/null | xargs grep -IEl 'nezha|dashboard|grpc|client_secret|server' 2>/dev/null | while IFS= read -r f; do
if ! grep -Eq "$TRUSTED_NEZHA_ADDR_REGEX" "$f" 2>/dev/null; then
echo "$f"
fi
done | grep . >/tmp/nezha_ioc_untrusted.$$; then
warn "以下哪吒相关文件未匹配 TRUSTED_NEZHA_ADDR_REGEX,需核查是否连到第三方主控:"
sed 's/^/ /' /tmp/nezha_ioc_untrusted.$$ | head -n 80
fi
rm -f /tmp/nezha_ioc_untrusted.$$ 2>/dev/null
else
info "未设置 TRUSTED_NEZHA_ADDR_REGEX,跳过哪吒主控地址白名单校验"
fi
[ "$found_nezha" -eq 0 ] && ok "未发现明显哪吒残留"
section "5" "XMRig / c3pool 挖矿"
found_miner=0
if [ -e /root/c3pool ]; then
alert "/root/c3pool 存在"
found_miner=1
fi
if pgrep -af 'xmrig|c3pool|monero|xmr|stratum|pool.supportxmr|nanopool|minexmr' >/tmp/nezha_ioc_miner_ps.$$ 2>/dev/null; then
alert "发现疑似挖矿相关进程:"
sed 's/^/ /' /tmp/nezha_ioc_miner_ps.$$
found_miner=1
fi
rm -f /tmp/nezha_ioc_miner_ps.$$ 2>/dev/null
for f in
/etc/systemd/system/c3pool_miner.service
/lib/systemd/system/c3pool_miner.service
/usr/lib/systemd/system/c3pool_miner.service
do
if [ -e "$f" ]; then
alert "发现 c3pool_miner service:$f"
print_file_preview "$f" 30
found_miner=1
fi
done
if find /etc/systemd/system /lib/systemd/system /usr/lib/systemd/system
-maxdepth 1 -type f 2>/dev/null | xargs grep -IEl 'xmrig|c3pool|stratum|monero|supportxmr|minexmr' 2>/dev/null | grep . >/tmp/nezha_ioc_miner_units.$$; then
alert "systemd unit 中发现挖矿关键词:"
sed 's/^/ /' /tmp/nezha_ioc_miner_units.$$
found_miner=1
fi
rm -f /tmp/nezha_ioc_miner_units.$$ 2>/dev/null
[ "$found_miner" -eq 0 ] && ok "未发现常见 XMRig / c3pool 挖矿痕迹"
section "6" "systemlog / SystemLoger 守护复活服务"
found_systemlog=0
if pgrep -af 'SystemLoger|systemlog' >/tmp/nezha_ioc_systemlog_ps.$$ 2>/dev/null; then
alert "发现疑似 systemlog / SystemLoger 进程:"
sed 's/^/ /' /tmp/nezha_ioc_systemlog_ps.$$
found_systemlog=1
fi
rm -f /tmp/nezha_ioc_systemlog_ps.$$ 2>/dev/null
for p in
/opt/systemlog
/etc/systemd/system/systemlog.service
/lib/systemd/system/systemlog.service
/usr/lib/systemd/system/systemlog.service
do
if [ -e "$p" ]; then
alert "发现守护/复活服务痕迹:$p"
[ -f "$p" ] && print_file_preview "$p" 40
found_systemlog=1
fi
done
if find /etc/systemd/system /lib/systemd/system /usr/lib/systemd/system
-maxdepth 1 -type f 2>/dev/null | xargs grep -IEl 'SystemLoger|/opt/systemlog|systemlog' 2>/dev/null | grep . >/tmp/nezha_ioc_systemlog_units.$$; then
alert "systemd unit 中发现 systemlog/SystemLoger 关键词:"
sed 's/^/ /' /tmp/nezha_ioc_systemlog_units.$$
found_systemlog=1
fi
rm -f /tmp/nezha_ioc_systemlog_units.$$ 2>/dev/null
[ "$found_systemlog" -eq 0 ] && ok "未发现 systemlog / SystemLoger 守护痕迹"
section "7" "SSH authorized_keys 后门"
found_sshkey=0
check_auth_keys_file() {
ak="$1"
owner_hint="$2"
[ -f "$ak" ] || return 0
count="$(grep -Ec '^(ssh-rsa|ssh-ed25519|ecdsa-sha2-|sk-ssh-|sk-ecdsa-)' "$ak" 2>/dev/null || true)"
info "$owner_hint authorized_keys 公钥数: $count ($ak)"
if grep -Eiq 'gary|c3pool|nezha|xmrig|systemlog|SystemLoger|backdoor|root@.*ubuntu|root@.*debian' "$ak" 2>/dev/null; then
alert "$owner_hint authorized_keys 含可疑注释或关键词:$ak"
grep -Ein 'gary|c3pool|nezha|xmrig|systemlog|SystemLoger|backdoor|root@.*ubuntu|root@.*debian' "$ak" 2>/dev/null | sed 's/^/ /'
found_sshkey=1
fi
if awk 'length($0) > 900 {print NR ":" substr($0,1,160) "..."}' "$ak" 2>/dev/null | grep . >/tmp/nezha_ioc_longkey.$$; then
warn "$owner_hint authorized_keys 存在超长行,需确认是否为合法证书型 key:$ak"
sed 's/^/ /' /tmp/nezha_ioc_longkey.$$
found_sshkey=1
fi
rm -f /tmp/nezha_ioc_longkey.$$ 2>/dev/null
if grep -Ev '^\s*$|^\s*#|^(command=|from=|environment=|no-|permit|restrict|ssh-rsa|ssh-ed25519|ecdsa-sha2-|sk-ssh-|sk-ecdsa-)' "$ak" 2>/dev/null | grep . >/tmp/nezha_ioc_badkeyline.$$; then
warn "$owner_hint authorized_keys 有格式异常行:$ak"
sed 's/^/ /' /tmp/nezha_ioc_badkeyline.$$
found_sshkey=1
fi
rm -f /tmp/nezha_ioc_badkeyline.$$ 2>/dev/null
}
check_auth_keys_file "/root/.ssh/authorized_keys" "root"
while IFS=: read -r user _ uid _ _ home shell; do
[ -n "$user" ] || continue
[ -n "$home" ] || continue
case "$shell" in
/nologin|/false) ;;
*)
if [ "$home" != "/root" ]; then
check_auth_keys_file "$home/.ssh/authorized_keys" "$user"
fi
;;
esac
done < /etc/passwd
if [ "$found_sshkey" -eq 0 ]; then
ok "未发现带常见可疑关键词的 authorized_keys;仍建议逐把人工确认"
fi
section "8" "cron / anacron 持久化"
found_cron=0
for u in $(cut -d: -f1 /etc/passwd 2>/dev/null); do
cron_content="$(crontab -l -u "$u" 2>/dev/null | grep -Ev '^\s*#|^\s*$' || true)"
if [ -n "$cron_content" ]; then
warn "用户 $u 存在 cron,需核查:"
printf '%s\n' "$cron_content" | sed 's/^/ /'
found_cron=1
fi
done
if find /etc/cron.d /etc/cron.daily /etc/cron.hourly /etc/cron.weekly /etc/cron.monthly /var/spool/cron /var/spool/cron/crontabs
-type f 2>/dev/null | xargs grep -IEn 'curl|wget|base64|/tmp/|/dev/shm|nc |ncat|bash -c|sh -c|python|perl|php|chmod +x|xmrig|c3pool|systemlog|nezha' 2>/dev/null | grep . >/tmp/nezha_ioc_cronfiles.$$; then
alert "系统 cron 文件含高风险关键词:"
sed 's/^/ /' /tmp/nezha_ioc_cronfiles.$$ | head -n 120
found_cron=1
fi
rm -f /tmp/nezha_ioc_cronfiles.$$ 2>/dev/null
[ "$found_cron" -eq 0 ] && ok "未发现明显 cron 持久化"
section "9" "systemd service / timer 持久化"
found_systemd=0
if have_cmd systemctl; then
if systemctl list-timers --all --no-pager 2>/dev/null | grep -Ei 'systemlog|nezha|xmrig|c3pool|tmp|shm|curl|wget' >/tmp/nezha_ioc_timers.$$; then
warn "发现可疑 systemd timer:"
sed 's/^/ /' /tmp/nezha_ioc_timers.$$
found_systemd=1
fi
rm -f /tmp/nezha_ioc_timers.$$ 2>/dev/null
if systemctl list-units --type=service --all --no-pager 2>/dev/null | grep -Ei 'systemlog|SystemLoger|nezha-agent-[a-z0-9]+|c3pool|xmrig' >/tmp/nezha_ioc_services.$$; then
alert "发现可疑 systemd service:"
sed 's/^/ /' /tmp/nezha_ioc_services.$$
found_systemd=1
fi
rm -f /tmp/nezha_ioc_services.$$ 2>/dev/null
else
info "未找到 systemctl,跳过 systemd 运行状态检查"
fi
if find /etc/systemd/system /lib/systemd/system /usr/lib/systemd/system
-maxdepth 1 -type f 2>/dev/null | xargs grep -IEn 'curl|wget|base64|/tmp/|/dev/shm|bash -c|sh -c|xmrig|c3pool|systemlog|SystemLoger|nezha-agent-[a-z0-9]+|config-[a-z0-9]+.ya?ml' 2>/dev/null | grep . >/tmp/nezha_ioc_systemd_files.$$; then
alert "systemd unit 文件含高风险关键词:"
sed 's/^/ /' /tmp/nezha_ioc_systemd_files.$$ | head -n 160
found_systemd=1
fi
rm -f /tmp/nezha_ioc_systemd_files.$$ 2>/dev/null
[ "$found_systemd" -eq 0 ] && ok "未发现明显 systemd 持久化"
section "10" "ld.so.preload 劫持"
if [ -f /etc/ld.so.preload ]; then
alert "/etc/ld.so.preload 存在,默认通常不应存在,需确认是否被劫持:"
print_file_preview /etc/ld.so.preload 50
while IFS= read -r sofile; do
[ -z "$sofile" ] && continue
case "$sofile" in
#*) continue ;;
esac
done < /etc/ld.so.preload
else
ok "未发现 /etc/ld.so.preload"
fi
section "11" "可疑网络连接"
found_net=0
if have_cmd ss; then
ss -tunap 2>/dev/null | grep -Ei 'xmrig|c3pool|nezha|systemlog|SystemLoger|:3333|:4444|:5555|:7777|:14444|:18080|:19999' >/tmp/nezha_ioc_net.$$
elif have_cmd netstat; then
netstat -tunap 2>/dev/null | grep -Ei 'xmrig|c3pool|nezha|systemlog|SystemLoger|:3333|:4444|:5555|:7777|:14444|:18080|:19999' >/tmp/nezha_ioc_net.$$
else
: > /tmp/nezha_ioc_net.$$
info "未找到 ss/netstat,跳过网络连接检查"
fi
if [ -s /tmp/nezha_ioc_net.$$ ]; then
warn "发现可能相关的网络连接或监听:"
sed 's/^/ /' /tmp/nezha_ioc_net.$$ | head -n 120
found_net=1
fi
rm -f /tmp/nezha_ioc_net.$$ 2>/dev/null
if have_cmd ss; then
if ss -tunap 2>/dev/null | awk '
/ESTAB/ && $5 !~ /^(127.0.0.1|::1)/ {
print
}' | grep -Ei ':(3333|4444|5555|7777|14444|18080|19999)\b' >/tmp/nezha_ioc_poolports.$$; then
alert "发现常见矿池端口连接:"
sed 's/^/ /' /tmp/nezha_ioc_poolports.$$
found_net=1
fi
rm -f /tmp/nezha_ioc_poolports.$$ 2>/dev/null
fi
[ "$found_net" -eq 0 ] && ok "未发现明显挖矿/后门相关网络连接关键词"
section "12" "高风险目录近期变动"
found_recent=0
if ! printf '%s' "$SINCE_DAYS" | grep -Eq '^[0-9]+$'; then
SINCE_DAYS=14
fi
for dir in
/tmp
/var/tmp
/dev/shm
/opt
/root
/etc/systemd/system
/var/spool/cron
/var/spool/cron/crontabs
do
[ -d "$dir" ] || continue
if find "$dir" -xdev -type f -mtime "-$SINCE_DAYS"
( -perm -111 -o -name '.service' -o -name '.timer' -o -name '.sh' -o -name '.yml' -o -name '*.yaml' )
2>/dev/null | head -n 80 | grep . >/tmp/nezha_ioc_recent.$$; then
warn "$dir 最近 $SINCE_DAYS 天存在可执行/配置/service/timer 文件变动:"
while IFS= read -r f; do
ls -la "$f" 2>/dev/null | sed 's/^/ /'
done < /tmp/nezha_ioc_recent.$$
found_recent=1
fi
rm -f /tmp/nezha_ioc_recent.$$ 2>/dev/null
done
[ "$found_recent" -eq 0 ] && ok "未发现高风险目录近期明显可疑变动"
section "13" "账户与 sudo 权限粗查"
found_account=0
if awk -F: '($3 == 0) {print $1 ":" $3 ":" $6 ":" $7}' /etc/passwd 2>/dev/null | grep -v '^root:' >/tmp/nezha_ioc_uid0.$$; then
if [ -s /tmp/nezha_ioc_uid0.$$ ]; then
alert "发现非 root 的 UID 0 账号:"
sed 's/^/ /' /tmp/nezha_ioc_uid0.$$
found_account=1
fi
fi
rm -f /tmp/nezha_ioc_uid0.$$ 2>/dev/null
if grep -RInE 'NOPASSWD|ALL=(ALL(:ALL)?) ALL|/bin/bash|/bin/sh' /etc/sudoers /etc/sudoers.d 2>/dev/null | grep -vE '^\s*#' >/tmp/nezha_ioc_sudo.$$; then
warn "sudoers 中存在高权限规则,需核查是否为预期:"
sed 's/^/ /' /tmp/nezha_ioc_sudo.$$ | head -n 120
found_account=1
fi
rm -f /tmp/nezha_ioc_sudo.$$ 2>/dev/null
[ "$found_account" -eq 0 ] && ok "未发现明显异常 UID 0 账号;sudo 规则未命中高风险关键词"
section "14" "登录与认证日志线索"
if have_cmd last; then
info "最近登录记录 last -n 20:"
last -n 20 2>/dev/null | sed 's/^/ /'
else
info "未找到 last 命令"
fi
auth_log_found=0
for logf in /var/log/auth.log /var/log/secure; do
[ -f "$logf" ] || continue
auth_log_found=1
info "$logf 最近 SSH 登录/失败摘要:"
grep -Ei 'Accepted|Failed|Invalid user|authentication failure|sudo|session opened' "$logf" 2>/dev/null | tail -n 60 | sed 's/^/ /'
done
[ "$auth_log_found" -eq 0 ] && info "未找到 /var/log/auth.log 或 /var/log/secure"
section "15" "复活检查建议"
cat <<'EOF'
[说明] 如果前面命中了 memfd、kworker 伪装、deleted exe、systemlog、c3pool:
1. 先记录 PID、exe、cmdline、ppid、网络连接。
2. 人工 kill 可疑 PID 后,等待 30-60 秒再重新跑本脚本。
3. 如果 PID 变了又出现,说明仍有守护/复活点。
4. 被 root 控制过的机器,清理只能止血,最终建议滚动重装。
EOF
echo
echo "============================================================"
echo " 自查结果汇总"
echo "------------------------------------------------------------"
echo " 高危 ALERT : $ALERT_COUNT"
echo " 警告 WARN : $WARN_COUNT"
echo " 信息 INFO : $INFO_COUNT"
echo "============================================================"
if [ "$ALERT_COUNT" -gt 0 ]; then
echo " 结论: 命中高危 IOC。建议按已沦陷处理,隔离、取证、轮换凭据、滚动重装。"
exit 2
elif [ "$WARN_COUNT" -gt 0 ]; then
echo " 结论: 存在需要人工复核的可疑项。未直接确认沦陷,但不建议忽略。"
exit 1
else
echo " 结论: 未发现本脚本覆盖范围内的已知 IOC。注意这不等于绝对安全。"
exit 0
fi
脚本只读自查,不负责清理
命中 [高危] 不等于自动删除,先取证再处理。
跑完没报警也不代表机器绝对干净
root 被控过后,最终可信方案还是重装;脚本只能覆盖已知 IOC。
先堵入口再清理
Dashboard/Agent 不处理,清掉后门也可能被重新下发。
ai吗
@Na #1 让ai帮我优化了一下脚本,希望能帮到大家处理问题,确实是搞了一天才弄好
涨知识了
测了下没有中
@zxxuu #4 那就好,我是搞了一天才弄好,只是暂时止血了,后面还要重装
唉。。。终于还是发生了!
学习一下
@treasureu #5 我看大规模中招也就这两天 我是11号升级的 版本 日志里边 这两天也有被扫的记录
测了,没中,还好还好,并且如果没开webssh这次漏洞是不是都没那么严重
@zxxuu #8 是的,我的就是13号晚上中招的,今天才查出来