@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
exe="$(safe_readlink "/proc/$pid/exe")"
ppid="$(get_ppid "$pid")"
cmd="$(get_cmdline "$pid")"
uid="$(get_uid_of_pid "$pid")"
user="$(get_user_by_uid "$uid")"
[ -z "$user" ] && user="$uid"
if [ -n "$exe" ]; then
if ! printf '%s' "$comm" | grep -Eq "$EXCLUDE_COMM_REGEX"; then
alert "疑似伪装内核线程 PID=$pid PPID=$ppid USER=$user COMM=$comm EXE=$exe CMD=${cmd:-<empty>}"
found_fake_kernel=1
fi
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"
clean_exe="$(printf '%s' "$exe" | sed 's/ (deleted)$//')"
if printf '%s' "$clean_exe" | grep -Eq "$ALLOW_DELETED_EXE_REGEX"; then
warn "PID=$pid USER=$user COMM=$comm EXE=$exe 属于白名单路径,但仍建议核查是否为升级残留"
else
alert "PID=$pid USER=$user COMM=$comm EXE=$exe CMD=${cmd:-<empty>}"
fi
found_deleted_exe=1
;;
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
if printf '%s\n' "$cron_content" | grep -Eiq 'curl|wget|base64|/tmp/|/dev/shm|nc |ncat|bash -c|sh -c|python|perl|php|chmod \+x|xmrig|c3pool|systemlog|nezha'; then
alert "用户 $u 的 cron 含高风险命令关键词"
fi
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
if [ -e "$sofile" ]; then
info "preload 目标存在:$sofile"
ls -la "$sofile" 2>/dev/null | sed 's/^/ /'
else
warn "preload 目标不存在:$sofile"
fi
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 不处理,清掉后门也可能被重新下发。