哪吒有点闹心,根据treasureu大佬修改的哪吒自检脚本

whenley31 2026-06-17 09:16 1

谢谢treasureu大佬,用ai又完善了下。

增加了注释,排除了一些常用程序,增加root登录ip归属地检测

如果不想用ip检测,直接bash就行,想用ip检测脚本前面用 ENABLE_GEO=1 bash

又增加了色彩和汇总让人类可以看懂,没时间捣鼓了,需要功能的让ai写吧


懒人指令

① GitHub raw

bash <(curl -fsSL https://raw.githubusercontent.com/heggoodtrefz680-prog/xiadaogu/main/nezha_ioc_check.sh)


② jsDelivr CDN国内更快

bash <(curl -fsSL https://cdn.jsdelivr.net/gh/heggoodtrefz680-prog/xiadaogu@main/nezha_ioc_check.sh)


③ 带 IP 归属查询

ENABLE_GEO=1 bash <(curl -fsSL https://cdn.jsdelivr.net/gh/heggoodtrefz680-prog/xiadaogu@main/nezha_ioc_check.sh)


贴脚本内容


#!/bin/bash
# ============================================================
# 哪吒入侵 IOC 自查 v6 (2026-06-17)
# 用途: 排查哪吒(Nezha)探针漏洞批量入侵常见植入物
# 特点: 只读检测, 不删除/不修改任何东西; 命中项附「风险释义+处置建议」
# 登录板块带完整时间戳+登录方式; 可选 IP 归属查询
# 用法: bash nezha_ioc_check.sh
# 带归属: ENABLE_GEO=1 bash nezha_ioc_check.sh
# 可调环境变量:
# TRUSTED_NEZHA_ADDR_REGEX 可信哪吒主控域名正则(默认空; 设了才做白名单判定)
# CRON_WHITELIST_REGEX 可信 cron 行正则(默认含 xcat/宝塔/证书)
# SINCE_DAYS 高风险目录变动回溯天数(默认 14)
# ENABLE_GEO=1 开启登录 IP 归属查询(联网到 ip-api.com, 默认关闭)
# ============================================================
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:-}"
CRON_WHITELIST_REGEX="${CRON_WHITELIST_REGEX:-xcat Cron|/www/server/cron/|/opt/cert-agent/|certbot|acme\.sh|/www/wwwroot/}"
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/)}"
SYSTEMD_WHITELIST_REGEX="${SYSTEMD_WHITELIST_REGEX:-cloud-init|ifupdown|networking|flexcdn|rc-local|emergency|rescue|getty}"
SINCE_DAYS="${SINCE_DAYS:-14}"

# 颜色(输出到终端才上色; 重定向到文件或设 NO_COLOR 则关闭, 避免日志里全是转义码)
if [ -t 1 ] && [ -z "${NO_COLOR:-}" ]; then
C_R=$'\033[1;31m'; C_Y=$'\033[1;33m'; C_G=$'\033[1;32m'; C_DIM=$'\033[2m'; C_0=$'\033[0m'
else
C_R=''; C_Y=''; C_G=''; C_DIM=''; C_0=''
fi

print_line() { printf '%s\n' "------------------------------------------------------------"; }
section() { printf '\n[%s] %s\n' "$1" "$2"; print_line; }
info() { INFO_COUNT=$((INFO_COUNT + 1)); printf ' %s[信息]%s %s\n' "$C_DIM" "$C_0" "$*"; }
warn() { WARN_COUNT=$((WARN_COUNT + 1)); printf ' %s[警]%s %s\n' "$C_Y" "$C_0" "$*"; }
alert() { ALERT_COUNT=$((ALERT_COUNT + 1)); printf ' %s[高危]%s %s\n' "$C_R" "$C_0" "$*"; }
ok() { printf ' %s[未发现]%s %s\n' "$C_G" "$C_0" "$*"; }
# 命中时打印「危害」与「处置」详解
explain() {
printf ' ├─ ⚠ 危害: %s\n' "$1"
printf ' └─ ✔ 处置: %s\n' "$2"
}
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")"
[ -z "$exe" ] && [ "$ppid" = "2" ] && return 0; return 1
}
print_file_preview() {
file="$1"; max_lines="${2:-20}"
[ -f "$file" ] && sed -n "1,${max_lines}p" "$file" 2>/dev/null | sed 's/^/ /'
}

run_report() {
echo "============================================================"
echo " 哪吒入侵 IOC 自查 v6"
echo " 主机: $HOSTNAME_NOW"
echo " 时间: $DATE_NOW"
echo " 用户: $(id 2>/dev/null || echo unknown)"
echo "------------------------------------------------------------"
echo " 等级图例:"
echo " [高危] 命中已知恶意特征/强 IOC, 需立即按疑似沦陷处置"
echo " [警] 可疑或需人工复核(可能是残留/正常业务, 也可能是早期入侵)"
echo " [信息] 中性事实, 供你判断(如登录 IP、公钥数量)"
echo " [未发现] 该项未命中(注意: 不等于绝对安全)"
echo "============================================================"
[ "$IS_ROOT" -ne 1 ] && { warn "当前不是 root, 部分 /proc、authorized_keys、systemd 信息可能读不到; 建议用 root 跑。"; explain "非 root 运行会漏检, 可能把已沦陷误判为干净" "用 root 重新执行: sudo bash $0"; }

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)"
[ -n "$memfd_fds" ] && { warn "PID=$pid USER=$user COMM=$comm 存在 memfd fd, 需核查 CMD=${cmd:-<empty>}"; printf '%s\n' "$memfd_fds" | sed 's/^/ /'; found_memfd=1; }
fi
done
if [ "$found_memfd" -ne 0 ]; then
explain "进程只驻留内存、磁盘无文件, 常伪装成 [kworker], 多为挖矿/远控木马; 重启即消失, 极难取证" \
"记录 PID/CMD/外连IP → kill -9 该PID → 排查拉起它的 cron/systemd 持久化 → 30~60秒后重扫, 若复活说明守护未清"
else ok "未发现指向 memfd 的进程"; fi

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
is_probably_real_kernel_thread "$pid" && continue
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" ] && ! 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
done
if [ "$found_fake_kernel" -ne 0 ]; then
explain "真内核线程无 exe 且父进程为 PID2; 进程名伪装成 [kworker] 却有用户态 exe = 典型挖矿/后门隐藏" \
"核对 exe 路径与 cmdline 取证 → kill -9 → 清除对应持久化点 → 必要时阻断外联"
else ok "未发现伪装 kworker / 内核线程的用户态进程"; fi

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
if [ "$found_deleted_exe" -ne 0 ]; then
explain "程序运行后删自身文件以逃避查杀, 常见于无文件马/二阶段载荷(白名单路径多为软件升级中的正常现象)" \
"取证: cp /proc/PID/exe /root/eXX.bin → 查父进程与外联 → 非升级残留则 kill 并清持久化"
else ok "未发现 exe 指向 deleted 文件的进程"; fi

section "4" "哪吒 Agent 残留 / 主控指向"
found_nezha=0
if pgrep -af 'nezha|nezha-agent' >/tmp/nz_ps.$$ 2>/dev/null; then info "运行中的哪吒进程:"; sed 's/^/ /' /tmp/nz_ps.$$; fi
rm -f /tmp/nz_ps.$$ 2>/dev/null
if [ -f /opt/nezha/agent/config.yml ]; then
srv="$(grep -E '^[[:space:]]*server:' /opt/nezha/agent/config.yml 2>/dev/null | head -1 | sed 's/.*server:[[:space:]]*//')"
dce="$(grep -E '^[[:space:]]*disable_command_execute:' /opt/nezha/agent/config.yml 2>/dev/null | head -1 | sed 's/.*:[[:space:]]*//')"
if [ -z "$TRUSTED_NEZHA_ADDR_REGEX" ]; then
info "哪吒 agent 主控 = ${srv:-<空>} (未设可信白名单, 请自行确认是否你的面板) 命令执行禁用=$dce"
[ "$dce" != "true" ] && { warn "disable_command_execute 未设为 true"; explain "命令执行开启时, 主控(或被劫持的主控)可向本机下发任意命令=RCE" "在 config.yml 设 disable_command_execute: true 并重启 agent"; }
elif printf '%s' "$srv" | grep -Eq "$TRUSTED_NEZHA_ADDR_REGEX"; then
info "哪吒 agent 主控 = $srv (匹配可信域名, 正常) 命令执行禁用=$dce"
[ "$dce" != "true" ] && { warn "disable_command_execute 未设为 true"; explain "命令执行开启时, 主控(或被劫持的主控)可向本机下发任意命令=RCE" "在 config.yml 设 disable_command_execute: true 并重启 agent"; }
else
alert "哪吒 agent 主控 = ${srv:-<空>} 不在可信列表! 可能被改连第三方主控 (config.yml)"; found_nezha=1
explain "agent 连到陌生主控 = 攻击者可经哪吒通道向你所有节点下发命令、批量植入" \
"立即核对该域名是否你自己的面板; 若陌生: 停 agent、改回自己主控、轮换 client_secret、排查植入"
fi
fi
for d in /etc/systemd/system /lib/systemd/system /usr/lib/systemd/system; do
[ -d "$d" ] || continue
find "$d" -maxdepth 2 -name 'nezha-agent-*.service' 2>/dev/null | while IFS= read -r u; do
if [ -L "$u" ] && [ ! -e "$u" ]; then echo "DANGLING|$u -> $(safe_readlink "$u")"
elif [ -f "$u" ]; then echo "FILE|$u"; fi
done
done >/tmp/nz_units.$$
if [ -s /tmp/nz_units.$$ ]; then
while IFS='|' read -r kind detail; do
if [ "$kind" = "DANGLING" ]; then warn "断链残留(目标已删, 多为旧版卸载残留, 可清理): $detail"
else alert "存在随机后缀 nezha-agent 服务文件, 需核查: $detail"; found_nezha=1; fi
done < /tmp/nz_units.$$
fi
rm -f /tmp/nz_units.$$ 2>/dev/null
if find /opt/nezha -maxdepth 3 -type f \( -name 'config-*.yml' -o -name 'config-*.yaml' \) 2>/dev/null | grep . >/tmp/nz_cfg.$$; then
alert "发现随机后缀哪吒 config:"; sed 's/^/ /' /tmp/nz_cfg.$$; found_nezha=1
fi
rm -f /tmp/nz_cfg.$$ 2>/dev/null
if [ "$found_nezha" -ne 0 ]; then
explain "随机后缀 nezha-agent-XXXX.service / config-XXXX.yml 是本轮漏洞批量植入的典型特征" \
"对照确认非自己安装 → systemctl disable --now 该服务 → 删 unit/config → daemon-reload → 升级哪吒到修复版本"
else ok "哪吒相关未发现高危项(主控指向可信/无随机后缀植入)"; fi

section "5" "XMRig / c3pool 挖矿"
found_miner=0
[ -e /root/c3pool ] && { alert "/root/c3pool 存在"; found_miner=1; }
if pgrep -af 'xmrig|c3pool|monero|stratum|supportxmr|nanopool|minexmr' >/tmp/nz_miner.$$ 2>/dev/null; then alert "发现疑似挖矿进程:"; sed 's/^/ /' /tmp/nz_miner.$$; found_miner=1; fi
rm -f /tmp/nz_miner.$$ 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
[ -e "$f" ] && { alert "发现 c3pool_miner service: $f"; print_file_preview "$f" 30; found_miner=1; }
done
if find /etc/systemd/system -maxdepth 1 -type f 2>/dev/null | xargs grep -IEl 'xmrig|c3pool|stratum|monero|supportxmr|minexmr' 2>/dev/null | grep . >/tmp/nz_mu.$$; then alert "systemd unit 含挖矿关键词:"; sed 's/^/ /' /tmp/nz_mu.$$; found_miner=1; fi
rm -f /tmp/nz_mu.$$ 2>/dev/null
if [ "$found_miner" -ne 0 ]; then
explain "挖矿程序占满 CPU、拉高电费, 易被云厂商限速/封机, 且几乎必然伴随后门与复活机制" \
"停并删 service+二进制 → 清 cron/systemd 复活点 → 找入口(弱口令/未修复漏洞)并封堵 → 重扫确认"
else ok "未发现常见挖矿痕迹"; fi

section "6" "systemlog / SystemLoger 守护复活服务"
found_sl=0
if pgrep -af 'SystemLoger|systemlog' >/tmp/nz_sl.$$ 2>/dev/null; then alert "发现疑似 systemlog/SystemLoger 进程:"; sed 's/^/ /' /tmp/nz_sl.$$; found_sl=1; fi
rm -f /tmp/nz_sl.$$ 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
[ -e "$p" ] && { alert "发现守护/复活服务痕迹: $p"; [ -f "$p" ] && print_file_preview "$p" 40; found_sl=1; }
done
if [ "$found_sl" -ne 0 ]; then
explain "伪装成系统日志服务的守护进程, 专门负责把被你杀掉的挖矿/后门反复拉起, 是'杀不死'的根源" \
"systemctl disable --now systemlog → 删 /opt/systemlog 与 unit → daemon-reload → 连同被它守护的木马一起清"
else ok "未发现 systemlog / SystemLoger 守护痕迹"; fi

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 'c3pool|nezha|xmrig|systemlog|SystemLoger|backdoor' "$ak" 2>/dev/null; then
alert "$owner_hint authorized_keys 含可疑关键词: $ak"; grep -Ein 'c3pool|nezha|xmrig|systemlog|SystemLoger|backdoor' "$ak" 2>/dev/null | sed 's/^/ /'; found_sshkey=1
fi
if awk 'length($0) > 1200 {print NR ":" substr($0,1,120) "..."}' "$ak" 2>/dev/null | grep . >/tmp/nz_lk.$$; then warn "$owner_hint authorized_keys 存在超长行, 确认是否合法: $ak"; sed 's/^/ /' /tmp/nz_lk.$$; found_sshkey=1; fi
rm -f /tmp/nz_lk.$$ 2>/dev/null
}
check_auth_keys_file "/root/.ssh/authorized_keys" "root"
while IFS=: read -r u _ uid _ _ home shell; do
[ -n "$u" ] && [ -n "$home" ] || continue
case "$shell" in */nologin|*/false) ;; *) [ "$home" != "/root" ] && check_auth_keys_file "$home/.ssh/authorized_keys" "$u" ;; esac
done < /etc/passwd
if [ "$found_sshkey" -ne 0 ]; then
explain "攻击者写入自己的公钥即可免密 root 登录, 改密码也挡不住, 是最隐蔽的持久化" \
"逐把核对公钥归属(可比对 ~/.ssh 已知 key 指纹), 删除任何陌生公钥, 之后轮换全部密钥对"
else ok "authorized_keys 未命中可疑关键词(仍建议人工逐把确认公钥归属)"; fi

section "8" "cron 持久化"
found_cron=0
KW='curl|wget|base64|/tmp/|/dev/shm|nc |ncat|bash -c|sh -c|chmod \+x|xmrig|c3pool|systemlog|nezha|memfd'
for u in $(cut -d: -f1 /etc/passwd 2>/dev/null); do
cc="$(crontab -l -u "$u" 2>/dev/null | grep -Ev '^\s*#|^\s*$' || true)"; [ -z "$cc" ] && continue
hr="$(printf '%s\n' "$cc" | grep -Ev "$CRON_WHITELIST_REGEX" | grep -Ei "$KW" || true)"
[ -n "$hr" ] && { alert "用户 $u cron 含高风险命令(已排除业务白名单):"; printf '%s\n' "$hr" | sed 's/^/ /'; found_cron=1; }
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 "$KW" 2>/dev/null | grep -Ev "$CRON_WHITELIST_REGEX" | grep . >/tmp/nz_cf.$$; then alert "系统 cron 文件含高风险关键词(已排除白名单):"; sed 's/^/ /' /tmp/nz_cf.$$ | head -n 80; found_cron=1; fi
rm -f /tmp/nz_cf.$$ 2>/dev/null
if [ "$found_cron" -ne 0 ]; then
explain "定时任务定期 curl|bash 重新下载并运行木马, 是最常见的'清了又回来'复活点" \
"crontab -e 删除恶意行; 检查 /etc/cron.* 与 /var/spool/cron; 确认每条任务都是你的业务"
else ok "未发现可疑 cron 持久化"; fi

section "9" "systemd service / timer 持久化"
found_sd=0
if have_cmd systemctl; then
if systemctl list-units --type=service --all --no-pager 2>/dev/null | grep -Ei 'systemlog|SystemLoger|c3pool|xmrig' >/tmp/nz_sds.$$; then alert "发现可疑 systemd service:"; sed 's/^/ /' /tmp/nz_sds.$$; found_sd=1; fi
rm -f /tmp/nz_sds.$$ 2>/dev/null
fi
if find /etc/systemd/system -maxdepth 2 -type f 2>/dev/null | grep -Ev "$SYSTEMD_WHITELIST_REGEX" | while IFS= read -r f; do
grep -IEn 'curl|wget|base64|/tmp/|/dev/shm|xmrig|c3pool|systemlog|SystemLoger|memfd|nezha-agent-[a-z0-9]+|config-[a-z0-9]+\.ya?ml' "$f" 2>/dev/null | sed "s#^#$f:#"
done | grep . >/tmp/nz_sdf.$$; then alert "自建 systemd unit 含高风险关键词:"; sed 's/^/ /' /tmp/nz_sdf.$$ | head -n 80; found_sd=1; fi
rm -f /tmp/nz_sdf.$$ 2>/dev/null
if [ "$found_sd" -ne 0 ]; then
explain "恶意 service/timer 实现开机自启与定时复活, 比 cron 更隐蔽" \
"systemctl disable --now 该单元 → 删 /etc/systemd/system 下对应文件 → systemctl daemon-reload → 重扫"
else ok "未发现可疑 systemd 持久化"; fi

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
explain "该文件会让系统给所有程序全局注入恶意 .so, 可隐藏进程/文件/端口(rootkit 级), 普通排查全被骗过" \
"极高危: 立即取证该 .so → 多数情况已无法可信清理, 强烈建议备份数据后滚动重装系统"
else ok "未发现 /etc/ld.so.preload"; fi

section "11" "可疑网络连接 / 矿池端口"
found_net=0
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/nz_pp.$$; then alert "发现常见矿池端口连接:"; sed 's/^/ /' /tmp/nz_pp.$$; found_net=1; fi
rm -f /tmp/nz_pp.$$ 2>/dev/null
if ss -tunap 2>/dev/null | grep -Ei 'xmrig|c3pool|systemlog|SystemLoger' >/tmp/nz_nc.$$; then warn "网络连接命中可疑进程名:"; sed 's/^/ /' /tmp/nz_nc.$$; found_net=1; fi
rm -f /tmp/nz_nc.$$ 2>/dev/null
else info "未找到 ss, 跳过网络检查"; fi
if [ "$found_net" -ne 0 ]; then
explain "外连矿池端口或可疑进程的网络活动, 基本可确证恶意流量正在进行" \
"用 ss -tunap 找到对应 PID → 溯源进程与文件 → kill 并清理 → 防火墙临时阻断对端 IP"
else ok "未发现挖矿/后门相关网络连接"; fi

section "12" "高风险目录近期变动 (近 ${SINCE_DAYS} 天)"
found_recent=0
printf '%s' "$SINCE_DAYS" | grep -Eq '^[0-9]+$' || SINCE_DAYS=14
for dir in /tmp /var/tmp /dev/shm /opt /root /etc/systemd/system; 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 60 | grep . >/tmp/nz_rc.$$; then
warn "$dir 近 $SINCE_DAYS 天有可执行/配置/service 文件变动(逐一核对是否你本人操作):"
while IFS= read -r f; do ls -la "$f" 2>/dev/null | sed 's/^/ /'; done < /tmp/nz_rc.$$
found_recent=1
fi
rm -f /tmp/nz_rc.$$ 2>/dev/null
done
if [ "$found_recent" -ne 0 ]; then
explain "/tmp、/dev/shm、/opt 等常被当木马落地点; 近期新增可执行/service 可能是植入物(也可能是你正常运维)" \
"逐个核对修改时间与来源: 你的操作=忽略; 不认识的文件→file/head 看内容→取证后删除"
else ok "高风险目录近期无可疑变动"; fi

section "13" "账户与 sudo 粗查"
found_acc=0
if awk -F: '($3 == 0) {print $1}' /etc/passwd 2>/dev/null | grep -v '^root$' | grep . >/tmp/nz_u0.$$; then alert "发现非 root 的 UID 0 账号:"; sed 's/^/ /' /tmp/nz_u0.$$; found_acc=1; fi
rm -f /tmp/nz_u0.$$ 2>/dev/null
if [ "$found_acc" -ne 0 ]; then
explain "非 root 却 UID=0 的账号拥有完整 root 权限, 是隐藏的管理员后门账号" \
"确认非业务需要后: passwd -l 锁定或 userdel 删除该账号, 并排查它的创建时间/来源"
else ok "未发现异常 UID 0 账号"; fi

section "14" "登录来源 IP / 时间 / 归属 (人工确认是否本人)"
if have_cmd last; then
info "① 登录来源按次数汇总:"
last -n 80 -i 2>/dev/null | grep -vE '^\s*$|wtmp begins|reboot|shutdown' | awk '{print $1, $3}' | sort | uniq -c | sort -rn | sed 's/^/ /'
echo " ② 各来源 IP 最近一次登录时间(完整时间戳):"
last -i -F 2>/dev/null | grep -vE '^\s*$|wtmp begins|reboot|shutdown|^$' | awk '$3 ~ /[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+|:/ && !seen[$3]++ {printf "%-18s %s %s %s %s %s\n",$3,$4,$5,$6,$7,$8}' | head -25 | sed 's/^/ /'
if ls /var/log/auth.log* >/dev/null 2>&1 || ls /var/log/secure* >/dev/null 2>&1; then
echo " ③ 最近成功登录(含方式 publickey/password):"
grep -hE "Accepted (publickey|password) for " /var/log/auth.log* /var/log/secure* 2>/dev/null | awk '{ts=$1; if($1 ~ /^[A-Z][a-z][a-z]$/) ts=$1" "$2" "$3; m="";u="";ip=""; for(i=1;i<=NF;i++){if($i=="Accepted")m=$(i+1); if($i=="for")u=$(i+1); if($i=="from")ip=$(i+1)} printf "%s %-9s user=%-10s from=%s\n", ts, m, u, ip}' | tail -15 | sed 's/^/ /'
pw=$(grep -hE "Accepted password for root" /var/log/auth.log* /var/log/secure* 2>/dev/null | wc -l | tr -d ' ')
[ "${pw:-0}" -gt 0 ] && { warn "存在 $pw 次 root【密码】登录成功记录"; explain "若你平时只用密钥登录, 出现 password 成功登录 = 密码可被爆破/已泄露" "确认是否本人; 建议 sshd 关闭密码登录: PasswordAuthentication no 后 systemctl restart sshd"; }
fi
explain "若出现你不认识的 IP 成功登录 root, 说明凭据已泄露/已被登入(本项不自动判定, 需你肉眼确认)" \
"确认存在陌生 IP: 立即改 root 密码+轮换 SSH key → who/w 查在线会话并踢出 → 检查 sshd 配置是否被放宽"
else info "未找到 last 命令"; fi
# ④ 可选 IP 归属查询(默认关闭; ENABLE_GEO=1 才联网到 ip-api.com, 避免节点信息外泄)
if [ "${ENABLE_GEO:-0}" = "1" ] && have_cmd curl; then
echo " ④ 登录 IP 归属(ip-api.com, 仅 ENABLE_GEO=1 时查询):"
ips=$(last -n 120 -i 2>/dev/null | grep -vE 'wtmp begins|reboot|shutdown|^\s*$' | awk '{print $3}' | grep -E '^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$' | grep -vE '^(127\.|0\.0\.0\.0|10\.|192\.168\.|172\.(1[6-9]|2[0-9]|3[01])\.)' | sort -u)
for ip in $ips; do
printf ' %-18s => ' "$ip"
curl -s --max-time 6 "http://ip-api.com/line/$ip?fields=country,regionName,city,isp&lang=zh-CN" 2>/dev/null | tr '\n' ' '
echo
sleep 0.4
done
else
info "IP 归属查询未开启(如需联网查归属, 用: ENABLE_GEO=1 bash nezha_ioc_check.sh)"
fi

} # end run_report

# 运行报告, 经 awk: ①按本节最严重结果给分节标题上色 ②生成可读版汇总(列出具体哪几项)
run_report | awk -v R="$C_R" -v Y="$C_Y" -v G="$C_G" -v Z="$C_0" '
function flush( i,sev,col){
if(n>0){
sev="G"
for(i=0;i<n;i++){ if(index(buf[i],"[高危]")){sev="R";break} else if(index(buf[i],"[警]")){sev="Y"} }
col=G; if(sev=="R")col=R; else if(sev=="Y")col=Y
print col buf[0] Z
for(i=1;i<n;i++) print buf[i]
if(sev=="R") redt[nred++]=buf[0]; else if(sev=="Y") yelt[nyel++]=buf[0]; else ngreen++
}
n=0
}
/^\[[0-9]+\] / { flush(); buf[0]=$0; n=1; next }
{ if(n>0) buf[n++]=$0; else print }
END{
flush()
bar="============================================================"
dash="------------------------------------------------------------"
print ""; print bar
if(nred>0) print " 检测结论: " R "⛔ 发现高危问题,需尽快处理" Z
else if(nyel>0) print " 检测结论: " Y "⚠ 未见高危,但有可疑项需你人工核对" Z
else print " 检测结论: " G "✔ 未发现本脚本覆盖的已知风险" Z
print dash
if(nred>0){ print R " ⛔ 高危项(必须处理,看该项下方“处置”):" Z; for(i=0;i<nred;i++) print " " R redt[i] Z }
if(nyel>0){ print Y " ⚠ 可疑项(请人工核对,多为残留/正常业务):" Z; for(i=0;i<nyel;i++) print " " Y yelt[i] Z }
print G " ✔ 其余 " ngreen " 项未发现问题" Z
print dash
print " 怎么读: " R "红" Z "=确认有问题、要处理 " Y "黄" Z "=不一定是坏事但要你确认 " G "绿" Z "=该项没事"
print " 下一步: 先处理红色项 → 再核对黄色项 → 处理完重新运行本脚本, 看红/黄是否归零。"
print bar
}
'

最新回复 (6)
  • 七块五 06-17 09:17
    1

    请使用代码块老哥

    试了试很不错,希望老哥能加上颜色标注 ^-^ ^-^


    ^-^

  • aseenm 06-17 09:25
    2

    6啊

  • 520 06-17 09:34
    3

    最近一年的漏洞特别多,飞牛 青龙 哪吒

  • sunyajiecc 06-17 09:38
    4

    漏洞修复的怎么样了?

  • whenley31 楼主 06-17 09:59
    5

    开cf依然会被黑,除非可以屏蔽除cf ip段的所有ip

  • whenley31 楼主 06-17 13:25
    6

    这个检测还有缺少,应该要查查本地config访问日志以及sql的访问下载日志,看看是否有可疑,好像这个有的已经提权但是一直潜伏着,这样判断要准确点

* 帖子来源NodeSeek
返回