服务器被 Mirai 木马入侵:一次完整的应急响应实录
作为独立开发者(一人公司),我的云服务器被 Mirai 僵尸网络木马入侵。本文记录了从发现告警、溯源分析、应急清理到安全加固的完整过程,希望能为同样使用云服务器的开发者提供一份实用的安全参考。
一、事件发现
腾讯云安全中心弹出了一条高危告警:
- 病毒类型:
Linux.Backdoor.Agent.Ssmw(Mirai 变种) - 恶意行为:
wget正从外部 IP(84.247.128.162)下载名为rbot的恶意文件到项目目录/root/projects/flower-shop/apps/server/ - 攻击目的: 将服务器加入僵尸网络,用于发起 DDoS 攻击或扫描其他设备
关键线索: 恶意文件被直接下载到 /root 目录下,说明攻击者已获取系统最高权限。
二、入侵路径分析
Mirai 木马的典型入侵方式有以下几种。结合我服务器的实际情况,逐一排查:
1. 弱口令 / SSH 暴力破解
Mirai 通过扫描弱密码感染设备。如果 SSH 使用默认 22 端口 + 简单密码,自动化脚本几秒内就能暴力破解。
我的密码强度实际上不低,所以这不一定是唯一入口。但密码登录本身就是风险敞口。
2. 暴露的端口与服务
除了 SSH,如果还运行了 Redis、MySQL 或 Node.js 应用等服务,且存在未授权访问或已知漏洞,攻击者可以借此突破。
3. 应用层漏洞(RCE)
如果 Web 应用代码中存在文件上传漏洞或命令注入漏洞,攻击者可以直接通过应用执行 wget 下载木马,然后利用本地提权获取 root 权限。
4. 即使密码复杂,也可能被窃取的几种途径
- SSH 私钥泄露: 私钥保存在不安全的地方,或本地开发机中了马
- 应用层 RCE: 攻击者通过 Web 漏洞进来,不需要密码
- 侧向渗透: 同一台服务器上某个旧服务(如未授权的 Redis / Docker API)存在漏洞,攻击者从一个"窗户"爬进来后横向移动
三、入侵痕迹取证
3.1 定时任务中的"定时炸弹"
执行 crontab -l 发现了一条恶意持久化指令:
@reboot /root/.config/sys-update-daemon -name shop.laohuoji.link >/dev/null 2>&1
这条指令的特征:
| 特征 | 说明 |
|---|---|
| 伪装性 | 文件名伪装成系统更新守护进程 sys-update-daemon,藏在隐藏目录 .config 下 |
| 持久化 | @reboot 意味着每次重启服务器都会自动启动木马 |
| 定向标记 | 域名 shop.laohuoji.link 说明攻击者对目标做了标记 |
3.2 Git 状态异常——文件权限被批量篡改
在服务器上执行 git status,发现大量核心配置文件被标记为 modified:
modified: README.md
modified: components.json
modified: ecosystem.config.js
modified: next.config.mjs
modified: package.json
modified: pnpm-lock.yaml
modified: postcss.config.mjs
modified: tailwind.config.js
modified: tsconfig.json
modified: update-admin-password.mjs
通过 git diff 排查发现:代码内容并未被修改,但文件权限从 644(普通文件)被改为 755(可执行)。
diff --git a/apps/server/package.json b/apps/server/package.json
old mode 100644
new mode 100755
推测原因: 攻击者在批量执行 chmod +x 时,把整个项目目录的文件都改成了可执行权限,以便恶意程序能顺利运行。
3.3 /tmp 目录中的可疑残留
find /tmp -type f
发现以下异常文件:
- 高度可疑 — 以 MD5 字符串为目录名的
sw.js文件(共 4 个),疑似是攻击者用于 Web 劫持的 Service Worker 残留 - 异常文件 —
/tmp/ac3ebce8和/tmp/bc7078b6,随机 8 位文件名、无后缀,通常是恶意程序的临时指令文件
3.4 ZSH 历史记录——还原"行军路线"
通过 tail -n 200 ~/.zsh_history 分析:
- 历史中没有任何用户主动下载
rbot或设置sys-update-daemon的指令 rbot进程以 root 权限在后台运行,确认为外部注入而非误操作- 发现
pnpm-lock.yaml也曾被篡改,可能是攻击者尝试供应链攻击(诱导pnpm install时下载带后门的依赖包)
3.5 网络连接审计
netstat -antp | grep ESTABLISHED
结果显示所有连接均为正常服务:
tat_agent/YDService:腾讯云自带的监控服务(内网地址169.254.x.x)sshd: root@:当前操作者的 SSH 连接
结论:没有可疑的外联 IP,隐藏后门已被清除。
四、应急处理步骤
第一步:阻断攻击入口
- 修改 SSH 配置,关闭密码登录,强制使用密钥:
vim /etc/ssh/sshd_config # 设置以下两项: # PasswordAuthentication no # PubkeyAuthentication yes systemctl restart sshd - 检查 authorized_keys,确保只有自己的公钥:
cat /root/.ssh/authorized_keys
第二步:清除持久化后门
- 删除恶意定时任务:
crontab -e # 删除包含 @reboot ... sys-update-daemon 的那一行 # 保留腾讯云监控 stargate 的那行 - 杀死恶意进程并删除文件:
pkill -f sys-update-daemon rm -rf /root/.config/sys-update-daemon rm -f /root/projects/flower-shop/apps/server/rbot
第三步:恢复代码完整性
- 利用 Git 强制回滚,重置所有文件权限和内容:
git reset --hard HEAD git clean -fd - 确认
git status显示working tree clean
第四步:清理临时文件
rm -rf /tmp/*sw.js
rm -f /tmp/ac3ebce8 /tmp/bc7078b6
rm -rf /tmp/84eaac9ce9f32b023dfa052d9f13ff03*
第五步:重新部署项目(最彻底方案)
rm -rf flower-shop
git clone git@github.com:dohard-ma/flower-shop.git
cd flower-shop/apps/server
pnpm i
原则: 既然已经被入侵,与其一个文件夹一个文件夹检查,不如直接删除项目目录重新 clone 干净代码。如果条件允许,直接重装系统是最稳妥的选择。
五、事后加固:建立监控预警
为了防止类似事件再次发生且不被发现,我用一个简单的 Shell 脚本 + 飞书 Webhook 搭建了自动巡检:
监控脚本 ~/scripts/monitor_project.sh
#!/bin/bash
# --- 配置区 ---
PROJECT_DIR="/root/projects/flower-shop"
WEBHOOK_URL="你的飞书/钉钉 Webhook 地址"
SERVER_NAME="生产服务器"
# --- 执行检查 ---
cd $PROJECT_DIR
# 1. 检查未追踪文件(排除 logs)
UNTRACKED=$(git ls-files --others --exclude-standard | grep -v "logs/")
# 2. 检查已追踪文件是否被篡改
MODIFIED=$(git diff --name-only)
if [ ! -z "$UNTRACKED" ] || [ ! -z "$MODIFIED" ]; then
REPORT=""
[ ! -z "$MODIFIED" ] && REPORT="$REPORT\n[被篡改文件]:\n$MODIFIED"
[ ! -z "$UNTRACKED" ] && REPORT="$REPORT\n[新文件]:\n$UNTRACKED"
MESSAGE="⚠️ 安全预警:[$SERVER_NAME] 发现代码变动!$REPORT"
# 使用 python3 处理 JSON 转义(避免换行符破坏 JSON 结构)
SAFE_MESSAGE=$(printf '%s' "$MESSAGE" | python3 -c 'import json,sys; print(json.dumps(sys.stdin.read()))')
curl -X POST -H "Content-Type: application/json" \
-d "{
\"msg_type\": \"text\",
\"content\": {
\"text\": $SAFE_MESSAGE
}
}" $WEBHOOK_URL
fi
设置定时巡检
crontab -e
# 添加以下内容,每小时检查一次:
0 * * * * /bin/bash /root/scripts/monitor_project.sh > /dev/null 2>&1
踩坑记录: 飞书 Webhook 对 JSON 格式要求严格。直接在
curl -d中拼接含换行符的字符串会导致Bad Request。解决方案是用python3 -c 'import json,sys; print(json.dumps(sys.stdin.read()))'对消息内容做 JSON 安全转义。
六、经验总结
安全清单(独立开发者必做)
| 优先级 | 措施 | 说明 |
|---|---|---|
| 🔴 P0 | 禁用 SSH 密码登录 | 设置 PasswordAuthentication no,强制密钥登录。这是防御 Mirai 等僵尸网络最有效的手段 |
| 🔴 P0 | 修改 SSH 默认端口 | 弃用 22 端口,改为 10000 以上的随机端口 |
| 🔴 P0 | 配置安全组 | 在云控制台中,仅允许常用 IP 访问 SSH 端口 |
| 🟡 P1 | 定期审计 authorized_keys |
确保只有自己的公钥,防止攻击者植入后门密钥 |
| 🟡 P1 | 检查应用层漏洞 | 排查代码中是否有 exec()、spawn() 等直接调用系统命令的地方 |
| 🟡 P1 | 确认 .env 不泄露 |
.env 必须在 .gitignore 中,不通过 Nginx 被公开访问 |
| 🟢 P2 | 建立自动巡检 | 通过 cron + Webhook 监控代码变动 |
| 🟢 P2 | 定期清理 | 每隔几个月执行 git clean -fd 确保无外部残留 |
核心原则
重装优于修补 — 系统被 root 入侵后,简单删除文件无法保证安全。攻击者可能已修改系统二进制文件(如
ls、ps)来隐藏自己。最稳妥的方案是备份数据后重装 OS。不要信任密码 — 无论密码多复杂,SSH 密钥登录才是终极防线。
Git 是最好的入侵检测器 — 生产服务器上保持
working tree clean状态,任何变动都可能是入侵信号。/tmp是攻击者的乐园 — 攻击者喜欢在/tmp、/var/tmp、/dev/shm等目录存放临时脚本,定期检查这些位置。密钥泄露比暴力破解更常见 — 高强度密码也挡不住 RCE 攻击或内网渗透,从源头封死入口才是根本。
附录:关键排查命令速查
# 查看定时任务
crontab -l
# 查看系统级定时任务
ls -la /etc/cron.d/
ls -la /etc/cron.daily/
cat /etc/crontab
# 查找可疑文件
find /tmp -type f
find /var/tmp -type f
# 检查当前网络连接
netstat -antp | grep ESTABLISHED
# 查看 Git 文件变动
git status
git diff --name-only
# 杀死可疑进程
ps -ef | grep <关键词>
pkill -f <进程名>
# Git 强制回滚
git reset --hard HEAD
git clean -fd