【复盘+自查脚本】哪吒面板漏洞被批量植入后门全过程:从发现到清理,附只读检测脚本

treasureu 2026-06-16 16:16 1

今天上午看到论坛有人发哪吒面板被入侵的帖子,回头一查自己也中了。折腾了一整天把十几台机器清了一遍,把过程和自查脚本整理出来,希望能帮到同样中招的兄弟。


一、事件概况



  • 入口:哪吒面板(Dashboard)漏洞。攻击者先攻破主控,再通过主控的"远程执行命令"功能,向所有挂在上面的 Agent 节点批量下发 payload。

  • 性质:自动化批量肉鸡攻击(图挖矿/代理变现),不是定向 APT。一晚上批量铺开,时间戳高度一致。

  • 关键点:攻击者全程没走 SSH。我 SSH 早就只留密钥、禁了密码登录,但这道防线根本没用上——因为 Agent 是以 root 跑的,主控让它执行啥就执行啥,不需要 SSH。所以"我关了密码登录为什么还中招"的答案是:它走的是另一扇门。


二、攻击者植入了什么(IOC 清单)



  1. 恶意哪吒 Agent:在 /opt/nezha/agent/ 下生成随机名 config-xxxxx.yml,里面的主控地址指向攻击者的 C2(不是你自己的主控)。

  2. SSH 后门公钥:往 authorized_keys 塞一把带特定注释的公钥做持久化。

  3. XMRig 挖矿:/root/c3pool/ 目录 + c3pool_miner.service 自启,连 c3pool 矿池。

  4. 守护/复活服务:systemlog.service + /opt/systemlog/,用来在你杀掉后门后把它重新拉起来。

  5. 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 复活。


四、处理流程(供参考)



  1. 堵入口:彻底删哪吒(stop+disable+rm /opt/nezha),这是根因,不删就还会被同样手法打。监控可以换 komari 之类没有命令执行能力的。

  2. 清植入物:逐台杀恶意 agent / 内存马、删后门公钥、删挖矿和守护服务、封 C2。

  3. 换锁:所有 SSH key 当泄露处理,重新生成、每台一把、清空重建 authorized_keys、禁用密码登录。

  4. 轮换凭据:被 root 控制过,理论上能读到的高价值凭据都换一遍(邮箱、CF、域名、面板等),密码库加 2FA。

  5. 取证复盘:查登录记录(last)、认证日志、账号(有没有影子 root)、文件改动时间线、cron、面板日志,确认有没有更深的痕迹。

  6. 重装:被 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 "=========================================="
最新回复 (37)
  • Na 06-16 16:18
    1

    ai吗

  • treasureu 楼主 06-16 16:24
    2

    @Na #1 让ai帮我优化了一下脚本,希望能帮到大家处理问题,确实是搞了一天才弄好 ^-^

  • qwaz130258 06-16 16:25
    3

    涨知识了

  • zxxuu 06-16 16:55
    4

    测了下没有中

  • treasureu 楼主 06-16 17:02
    5

    @zxxuu #4 那就好,我是搞了一天才弄好,只是暂时止血了,后面还要重装 ^-^

  • 二两俩 06-16 17:11
    6

    唉。。。终于还是发生了!

  • jackmashen 06-16 17:14
    7

    学习一下

  • zxxuu 06-16 17:18
    8

    @treasureu #5 我看大规模中招也就这两天 我是11号升级的 版本 日志里边 这两天也有被扫的记录

  • 空指针 06-16 17:29
    9

    测了,没中,还好还好,并且如果没开webssh这次漏洞是不是都没那么严重

  • treasureu 楼主 06-16 17:45
    10

    @zxxuu #8 是的,我的就是13号晚上中招的,今天才查出来

  • treasureu 楼主 06-16 17:46
    11

    @空指针 #9 没开这个的应该都没事吧?

  • 一七网络 06-16 18:09
    12

    好用 帮顶

  • treasureu 楼主 06-16 21:53
    13

    @一七网络 #12 ^-^

  • tablerjoy 06-16 22:43
    14

    这啥意思啊!


    哪吒入侵自查: debian 2026-06-16 22:41:59


    [1] memfd 内存马

    [2] kworker 伪装进程

    [3] 已删除文件执行

    [4] 恶意哪吒 Agent 残留

    root 1798473 0.0 2.5 1278256 25088 ? Ssl Jun13 1:39 /opt/nezha/agent/nezha-agent -c /opt/nezha/agent/config-q2r27.yml

    [警] 发现随机后缀 config 的 agent 进程

    nezha-agent-ffa3a06.service

    [警] 发现随机后缀 nezha service

    /opt/nezha/agent/config-q2r27.yml

    [警] 发现随机 config 文件

    [5] 挖矿程序

    [6] 守护复活服务

    [7] SSH 后门公钥

    (当前 authorized_keys 公钥数: )

    [8] 持久化(cron)

    [信息] 用户 root 有 cron(请核对):

    0 2 * * * docker exec nginx apk add logrotate && docker exec nginx logrotate -f /etc/logrotate.conf

    0 0 * * * ~/auto_cert_renewal.sh

    @reboot iptables-restore < /etc/iptables/rules.v4

    [9] ld.so.preload


    结论: 发现 [警] 项,请逐条人工核查

  • tablerjoy 06-16 22:48
    15

    是不是要删掉啊

  • hei 06-16 23:07
    16



    谢谢 好像没问题

  • kkbera 06-16 23:29
    17

    嚓,我说怎么一堆vps密码都改变了。原来是agent没disable command excute。赶紧重装。哎

  • sudoerx 06-16 23:31
    18

    赞 不错

    不如搞个选项 一键杀毒 需要多按两次确认

  • sanro 06-17 00:50
    19

    直接重装把杀不干净的谁知道入侵你的什么手法吃abuse就难受了,我重装一晚上了

  • luomozzz 06-17 01:26
    20

    好像表现都不一样,楼主这几个我都没遇到,我这有一个叫 xmrig 的挖矿病毒,会占用很高CPU内存,难道说是有好几个人在利用这个漏洞?

    我这也排查出的确就是哪吒漏洞导致的

  • 799814263 06-17 01:39
    21

    意思是主控要先出事才中招吗

  • 鸡不择食 06-17 01:46
    22

    完犊子。全部中招

  • spoiledcat 06-17 04:28
    23

    @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 不处理,清掉后门也可能被重新下发。

  • xyjsw 06-17 04:53
    24

    谢谢分享

  • 巨龙v 06-17 04:58
    25

    @treasureu #0 bd

  • qq4444 06-17 05:30
    26

    如果重新安装了系统,是不是就没事了,没敢安装哪吒了,只部署了自己的工具,现在没有探针跟瞎子一样,komari 安全吗。

  • JobsGood 06-17 06:25
    27

    标记一处地点

  • treasureu 楼主 06-17 06:57
    28

    @tablerjoy #14 注意看你这个随机config文件就说明已经中招了

  • treasureu 楼主 06-17 06:59
    29

    @sudoerx #18 考虑到大家情况都不一样就只做了排查,建议还是直接重装比较好

  • treasureu 楼主 06-17 07:00
    30

    @kkbera #17 我也是,当时根本没注意这个disable

  • treasureu 楼主 06-17 07:01
    31

    @sanro #19 主要是还有一些东西没备份,挺难受的,就怕备份里夹着什么东西就更恶心了

  • treasureu 楼主 06-17 07:03
    32

    @luomozzz #20 你可以看看ip是不是都是24开头那个,我是还没开始植入xmrig挖矿,cpu也都正常

  • treasureu 楼主 06-17 07:06
    33

    @qq4444 #26 重装一般就没事了,我用的komari,在安装agent的时候记得勾选禁用远程控制就可以

  • treasureu 楼主 06-17 07:07
    34

    @799814263 #21 就是通过主控进来的,可以查一下有没有多一个gary公钥

  • LogicNova 06-17 07:08
    35

    这两天扫得猛

  • imsnail 06-17 07:21
    36

    我机器上都屏蔽了主控执行命令,而且我的主控装在容器里,是不是就没事?

  • treasureu 楼主 06-17 07:26
    37

    @imsnail #36 那你这种应该挺安全的,但还是可以排查一下放心些,最近很多中招

* 帖子来源NodeSeek
返回