对一款Linux木马开源提示词的测评

admin 2026-09-28 05:18:54 网络安全文章 来源:ZONE.CI 全球网 0 阅读模式

文章总结: 本文测评了一款基于开源提示词生成的Linux内核木马(rootkit),其功能包括进程、文件、网络及模块隐藏,并具备持久化和关机回写能力。分析指出该木马脱胎于diamorphine并增强了网络隐藏与持久化,但存在多处暴露面,如持久化文件明文存放、网络隐藏可被ss检测。文章还提供了检测思路,并讨论了AI生成代码的局限性与蓝队应对策略。 综合评分: 82 文章分类: 恶意软件,红队,内网渗透,漏洞分析,安全运营


对一款Linux木马开源提示词的测评

刃无锋 刃无锋

利刃信安

2026年9月26日 17:30 天津

在小说阅读器读本章

去阅读

在公众号小说中沉浸阅读

对一款Linux木马开源提示词的测评

这是第二篇,上一篇《对一款Windows木马开源提示词的测评》是Windows版提示词的分析,这一篇则是对Linux版提示词的测评和分析。补上提示词链接:kernel-lab-specs: Implementation specs for AI agent kernel module

上一篇文章中对Windows版本的测试,从环境搭建到完成第一个Rootkit版本,花了3个多小时,而Linux版本则快得多,在给出了提示词文档后,只花了大概不到15分钟时间,就完成了整个项目的编写,当然,代码量也小了很多,我这边第一次生成,代码量大概1700行。功能也齐全,可以进行进程、文件、网络以及内核模块自身的隐藏,同时也有持久化和关机回写功能。从能力上来说,和Windows版几乎如出一辙,只是因为所处操作系统不同,在实现原理上大相径庭。并且设置了兼容层和配置,可以轻易的通过修改.h源码实现后续兼容性问题和rootkit配置问题。

源码分析

样本功能

Linux版的提示词为“crimson-kmod-agent-prompt.md”。文档中同样并未直接告诉AI生成的内核项目是什么名称,所以项目名称会很随机,我这边的项目名称叫crimson,项目能力和实现原理大致如下:

| 能力 | 实现 | | — | — | | 文件名前缀隐藏 | getdents /getdents64syscall hook + 缓冲区压缩 | | 进程隐藏 | task->flags 置 PF_INVISIBLE+ /proc枚举过滤 | | 模块自隐藏 | 模块链表摘除 + 名称过滤 | | TCP/UDP 端口隐藏 | ftrace hook {tcp,udp}{4,6}_seq_show | | 提权 | prepare_creds() + commit_creds()(compat.h``give_root()) | | 持久化 | .ko 写回 + 启动脚本 + systemd unit |

对LKM的控制如下:

kill -34 <pid> &nbsp; 切换进程在 /proc 中的可见性
kill -35 0 &nbsp; &nbsp; &nbsp; 把调用进程提升为 root
kill -36 0 &nbsp; &nbsp; &nbsp; 切换模块在 lsmod 中的可见性
kill -37 <port> &nbsp;切换端口在 /proc/net 中的可见性(端口号经 pid 参数传入)

对于红队工具研发人员以及蓝队资深取证人员来说,不得不说,很眼熟,尤其是在分析完源码后,几乎可以笃定,该项目应该是脱胎于著名rootkit工具Diamorphine(Diamorphine: LKM rootkit for Linux)。但Diamorphine并没有对网络进行隐藏,也没有持久化能力和关机回写能力,这玩意儿算是Diamorphine rootkit的一个增强变种。但这种增强是否真的有意义,可以多多探讨,文章后面我会进行分析。

配置层

所有配置都放在config.h中,方便统一配置的情况下,也方便了对固定取证线索的修改,从反取证来说,是个不错的策略。

image-20260923120535816

Hook方式

整个项目采用了两种hook方式,syscall hook和ftrace hook,该rootkit最核心的隐藏能力和通信能力,都是建立在这些hook上的。

通信的实现

通过syscall hook内核的kill函数,实现了对应用层kill命令的消息拦截和过滤,从而实现单项的内核通信

image-20260923122253929

image-20260923122321936

真正的控制函数为crimson_kill_ctl,通过传入的pid和signal信号进行判断

image-20260923122357357

进程隐藏

进程隐藏分两步:先标记,后过滤。标记发生在kill信号的拦截处,把目标进程的task->flags翻转一个PF_INVISIBLE位:

image-20260926145037231

PF_INVISIBLE(0x10000000)取自老内核sched.h里已废弃的位定义,现代内核自身不再使用,复用不会冲突——反过来说,“进程flags里出现内核已不使用的位”本身就是一条检测线索。过滤则复用了文件隐藏的getdents通道,只是对/proc根目录多一层判定:

image-20260926145123888

所以进程隐藏本质是文件隐藏的特例——藏的是/proc下以pid命名的目录项。

网络隐藏

网络隐藏是Diamorphine没有的能力,走ftrace通道,hook的是/proc/net/{tcp,tcp6,udp,udp6}的渲染函数:

static&nbsp;struct&nbsp;ftrace_hook&nbsp;net_hooks[] =&nbsp;{
&nbsp; &nbsp; HOOK("tcp4_seq_show", hacked_tcp4_seq_show, &orig_tcp4_seq_show),
&nbsp; &nbsp; HOOK("tcp6_seq_show", hacked_tcp6_seq_show, &orig_tcp6_seq_show),
&nbsp; &nbsp; HOOK("udp4_seq_show", hacked_udp4_seq_show, &orig_udp4_seq_show),
&nbsp; &nbsp; HOOK("udp6_seq_show", hacked_udp6_seq_show, &orig_udp6_seq_show),
};

kill -37 <port>把端口号经pid参数存进一张16项的隐藏表,hook函数对每个socket比较源、目的端口,命中就跳过该行不渲染:

image-20260926145246614

对/proc/net/tcp这一个视图,隐藏是有效的。但ss走的是netlink sock_diag,完全不经过seq_show,所以该方式防不住ss的检测。

文件/目录隐藏

文件隐藏是全项目实现最细致的部分。getdents的hook拿到原始返回缓冲后,把名字以crimson_魔术前缀开头的记录从缓冲区里“压”掉,压缩分首条与非首条两种情况:

image-20260926145448386

值得说的是它处理了“整批被隐藏”的边界——getdents是分批返回的,若某批记录全部命中过滤,直接返回0会被用户态当成EOF,后果不是藏了几个文件,而是目录被截断。实现是置retry标志重取下一批:

if&nbsp;(ret ==&nbsp;0) {
&nbsp; &nbsp; kfree(kdirent);
&nbsp; &nbsp; *retry =&nbsp;1;
&nbsp; &nbsp;&nbsp;return&nbsp;0;
}

关机回写

关机回写和持久化是相对Diamorphine的另一处增强,触发点有三个,互为冗余:reboot系统调用hook(main.c:633)、优先级INT_MAX的重启通知链、以及rmmod卸载。三个入口都汇到do_persistence:

image-20260926145607725

write_self_ko把模块加载时就读进内存的.ko原样写回ko_path——攻击者加载完就可以删掉磁盘上的样本,模块关机时自己把文件拼回来,达到“删文件不死”的效果。setup_autostart铺三层:/etc/crimson_load.sh、systemd unit加启用链接、rc.local追加。比较有意思的是启用链接的做法:

call_usermodehelper(mkdir_argv[0], mkdir_argv, envp, UMH_WAIT_PROC);
call_usermodehelper(ln_argv[0], ln_argv, envp, UMH_WAIT_PROC);

在.wants/目录下放真实文件不算启用,必须是符号链接;也没用systemctl enable——那要求运行中的manager认识这个unit,从内核态直接mkdir加ln -sf建链接下来得稳。实测靶机重启7.5秒后模块自动加载,自启与自隐藏无缝衔接。而systemd这条通道提示词里只字未提(rc.local出现了8次、systemd出现0次),是实测踩坑后AI自己补的。

但这也是全项目最大的暴露面:隐藏做到了getdents一级,/etc下三个文件却原样明摆在系统管理面上。持久化是这类木马脚印最大的一环。

如何检测

从整个实现这个木马把“枚举”堵得很严,除非直接知道隐藏了哪些文件、文件夹,或者知道模块名称,否则常规的枚举经检测不太容易发现端倪。

从之前的源码分析中可以发现,通过查看/etc目录下的可疑shell脚本以及查看rc.local是可以发现恶意持久化的,而且,通过ss与netstat的对比,也可以发现隐藏的网络连接。除此以外,通过测试,还发现有其他方式进行检测。在未知的情况下,可以尝试输入kill -36 0,查看有没有新的内核模块出现

image-20260926150739499

此外,由于项目本身利用ftrace hook来实现网络隐藏,那完全可以通过查看当前内核里已经挂载了哪些 ftrace 回调钩子

image-20260926151020560

通过以上方式也可以很快捷方便定位内核问题。

提示词中的坑

和上一篇一样,提示词的坑也不少。比如,syscall表改写静默失效的问题。内核6.11起x86分派改为x64_sys_call()内部switch直调__x64_sys_*,不再经过sys_call_table。表项确实写进去了(kcore读回是新值)、insmod成功、dmesg干净——三条“成功”信号全中,但其实功能是不生效的。

总结:增强是否有意义

回到开头留下的问题:相对Diamorphine的这些增强,是否有意义?我的看法是有限的。网络隐藏只挂seq_show一条通路,ss一比对就穿帮;持久化在/etc下留三处明文,是全项目暴露面积最大的一环。真正有工程价值的,反而是双通道syscall hook(表改写与ftrace wrapper并存,覆盖新旧两代内核)和“删盘不死”的关机回写。

对蓝队,这个样本把“单一数据源不可信”演练得很完整:ls对stat、ps对kill -0、/proc/net/tcp对ss、lsmod对/sys/module,每一组对照都是一条独立的发现路径。

对“提示词生成木马”这件事本身,两篇测评的结论是一致的:AI把“写出能编译的代码”的成本压到了分钟级,但它写的世界是提示词作者见过的世界——内核换了分派机制、CPU换了安全特性、发行版换了自启方式,它都不知道。门槛降低的是“能跑”,不是“能用”;而蓝队要防的,从来都是“能用”。

本文所有测试均在隔离的虚拟机靶场中完成,仅用于授权环境下的安全研究与防御检测研究。


免责声明:

本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。

任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。

本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我。

本文转载自:利刃信安 刃无锋 刃无锋《对一款Linux木马开源提示词的测评》

评论:0   参与:  0