故障排查与清理笔记:Nezha Agent 供应链攻击与脱机数据救援

1. 故障背景与根因定位

多台公网节点及通过 Tailscale 组网的无公网 IP 节点(含 PVE 宿主机)发生规模化资源耗尽与强制关机现象。初期在进行 Minecraft 服务端(7.6GB)打包下载时触发频繁宕机,后经溯源,确认为 Nezha Agent 供应链投毒攻击

入侵链路原理: > 攻击者利用漏洞攻陷了暴露在公网的哪吒监控中控端(Nezha Dashboard)。由于 Nezha Agent(探针)在各宿主机上以 root 权限运行,且其网络机制为 Agent 主动向 Dashboard 发起 WebSocket 长连接(出站流量)。攻击者借此合法通道下发恶意负载,此机制无视本地防火墙入站规则及 NAT 隔离,导致包括纯内网节点在内的所有挂载探针的机器全数沦陷。


2. 阶段一:业务数据脱机抢救(防 OOM/IO 崩溃)

在尝试使用 tar 或 SFTP 提取游戏服务端时,服务器频繁发生 Kernel Panic(内核恐慌)与断电。
解释: 感染状态下,木马与杀毒软件(clamd)已榨干系统内存,大批量碎文件(如 .mca)读取产生密集磁盘 I/O,瞬间撑爆缓存(Page Cache)触发系统自我保护机制强制关机。

为安全提取下图所示的 7.6GB 核心数据:

必须彻底避开已腐烂的操作系统环境,实施物理级脱机救援(Live CD 协议)。
解释: 操作系统如同房屋。当屋内布满陷阱(木马/死锁机制)时,我们不进屋,而是在院子里搭个临时帐篷(纯净 Live 镜像),直接从窗户把家具(数据)搬走。此时原系统的木马处于“休眠死肉”状态,绝对无法运行。

2.1 PVE 直通与光盘引导

  1. 将 64GB 物理 U 盘插入宿主机,在 PVE 硬件设置中执行 USB 直通:
  2. 挂载纯净 Ubuntu Server ISO 镜像,调整引导顺序为光驱优先。
  3. 启动虚拟机,选择 Try or Install Ubuntu Server,通过 Help -> Enter Shell 进入纯净命令行。

2.2 挂载与物理拷贝

识别逻辑卷与 U 盘挂载点(根据图示确认为 ubuntu--vg-ubuntu--lvsdb1):

执行挂载与数据抢救:

mkdir -p /mnt/oldsys
mkdir -p /mnt/usb
mount /dev/mapper/ubuntu--vg-ubuntu--lv /mnt/oldsys
mount /dev/sdb1 /mnt/usb
# cp (复制命令) -a (归档模式: 保留文件原权限) -r (递归: 拷贝所有层级) -v (详细模式: 输出过程)
cp -arv /mnt/oldsys/root/mc/* /mnt/usb/mc_backup/

2.3 乱码排错与安全卸载

拷贝过程中出现如 350212231 等八进制转义乱码。
1722
解释: 并非文件损坏或感染,纯粹因 Live 临时系统缺乏中文字库,系统将其转换为 UTF-8 底层字节码展示。

拷贝完成后必须执行强制刷写与安全卸载:

# sync (同步: 强制将内存中未写完的缓存数据写入物理 U 盘,防止拔盘损坏)
sync
# umount (卸载: 安全断开挂载点)
umount /mnt/usb

回到 Windows 验证,数据完整且中文名自动恢复:


3. 阶段二:感染特征与持久化机制分析

业务安全转移后,进入旧系统排查木马特征。

3.1 异常驻留进程


通过 ps aux 捕获到恶意进程:

root     1802545  0.4  0.1 1230740 7744 ?        Ssl  19:44   0:00 /dev/shm/.kw​ork‌er_‍u8

剖析:

  • 路径伪装: 位于 /dev/shm/(共享内存挂载点),运行在物理 RAM 中,躲避常规磁盘扫描且重启即焚。
  • 命名混淆: 进程名伪造为内核线程,并注入了零宽不可见字符(Zero-width characters)。这使得肉眼看着是常规字母,但直接手动输入进程名执行查杀会因字符不匹配而失效报错(No such file or directory)。

3.2 守护与自动复活闭环

利用 crontab -l 发现恶意定时任务:

*/30 * * * * /usr/freemem.sh >> /tmp/tmp.txt
* * * * * pgrep -x .k‍wor­ker​_u8 > /dev/null || /dev/shm/.k‍wor­ker​_u8 > /dev/null 2>&1 &

剖析: ` (每分钟执行一次),使用pgrep -x (精确匹配: 仅匹配进程名完全一致的目标)` 检查木马存活。

  • || (逻辑或: 若前置命令执行失败/找不到进程,则立即执行后置命令) 确保木马一旦被杀,1 分钟内必然从内存重新拉起分配新 PID。

4. 核心清理步骤(防复活查杀)

操作原则: 必须严格遵循“阻断守护 -> 击杀进程 -> 清理实体 -> 剥离通道”的逆向拆解顺序。

步骤 1:阻断 Crontab 守护(安全过滤法)

解释: 恶意任务含零宽字符,手动 crontab -e 极易误删或残留。严禁使用 echo 全盘覆盖法则,避免误删生产环境中未知的合法业务计划任务。使用管道过滤法进行精准剔除。

# grep (文本搜索) -v (反向匹配: 剔除包含目标字符串的行)
crontab -l | grep -v "/dev/shm" | grep -v "worker" | crontab -

执行后 crontab -l 确认脏代码已消失。

步骤 2:强制截断进程

无视零宽字符,通过片段模糊正则匹配进行击杀:

# pkill (通过名称终止进程) -f (完整命令行匹配: 忽略零宽字符干扰,强制终止包含片段的所有进程)
pkill -f "worker"
pkill -f "kwo"

步骤 3:铲除物理文件与残留脚本

清除内存挂载点实体及配套辅助脚本:

# rm (删除) -r (递归) -f (强制: 忽略不存在的文件,不提示确认)
rm -rf /dev/shm/.kw*
rm -f /usr/freemem.sh

步骤 4:剥离感染源 (Nezha Agent)

切断被攻击者用来下发指令的 WebSSH/探针通道:

# stop (停止运行); disable (禁用自启: 移除 systemd 软链接)
systemctl stop nezha-agent
systemctl disable nezha-agent
rm -f /etc/systemd/system/nezha-agent.service
# daemon-reload (重载守护进程: 让系统抹除对该服务的认知缓存)
systemctl daemon-reload
rm -rf /opt/nezha

5. 鉴权阻断与验证加固

5.1 清理后门密钥与重置凭据

攻击者获取 root 后必定写入免密公钥。执行硬阻断:

# > (重定向覆写: 物理清空文件内容至 0 字节,粉碎预留的免密隧道)
> ~/.ssh/authorized_keys
passwd root

5.2 验证

reboot

解释: 重启可强制卸载可能被木马挂载 (Hook) 的系统级内核模块,释放隐蔽子线程。重启后再次查验 ps auxcrontab -l,若无异常则节点止血成功。建议后续引入含独立 x86 网关和 DPI 深度包检测的三层网络架构隔离内网环境。

5.3 净室重建规范 (Clean Room Protocol)

解释: 鉴于 root 权限已失陷,系统内核及环境变量可能已被篡改。查杀木马仅为争取数据迁移时间的“止血”操作,必须执行零信任的物理级重装,彻底切断交叉感染链。

  • 数据提纯提取: 严禁整体打包 /var/www/opt 目录。仅提取纯净业务数据(如通过 mysqldump 导出的 .sql 文件、.mca 存档)。坚决遗弃旧系统的任何第三方脚本或可执行文件(.sh.bin),业务代码必须自官方或干净的 Git 库重拉。
  • 底层环境销毁: 依托 PVE 虚拟化隔离优势,直接从物理机控制台 Remove 销毁失陷虚拟机,物理抹除受污染的逻辑卷(LV)。随后挂载官方纯净镜像部署独立新实例。
  • 全链路凭据轮换: 弃用旧系统全部凭据。重新生成高强度密钥,重置所有数据库、Redis 及 API 的鉴权 Token。
  • 管控面板隐身: 针对高危 Web 控制台,严禁监听 0.0.0.0 暴露于公网。强制绑死 127.0.0.1 配合 Tailscale 内网穿透访问,实现公网维度物理隐身。

新系统挂载公网前,必须通过 sshd_config 锁死入口防线:

nano /etc/ssh/sshd_config

# Port (修改监听端口: 规避 22 端口的全网自动化盲扫,建议设为 10000 以上)
Port 38291
# PasswordAuthentication (禁用密码认证: 彻底阻断字典爆破,强制依赖非对称密钥鉴权)
PasswordAuthentication no
# PermitRootLogin (限制 Root 登录: 禁止密码直连 Root,切断最易被利用的提权路径)
PermitRootLogin prohibit-password

# 重载配置使规则生效
systemctl restart sshd