首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >用 eBPF 做网络层异常检测,从进程间通信里挖出隐蔽信道

用 eBPF 做网络层异常检测,从进程间通信里挖出隐蔽信道

作者头像
不吃草的牛德
发布2026-07-16 20:59:48
发布2026-07-16 20:59:48
430
举报
文章被收录于专栏:RustRust

防火墙、NIDS、流量镜像,全在岗。 可一台服务器上的两个进程,悄无声息地传了 2.1 GB 数据出去。 perimeter 上干干净净,什么都没看到。 最后抓住它的,还是那段跑在内核里的eBPF代码。


01 一个防火墙看不见的漏洞

先讲个红蓝对抗里的真事。

蓝队(防守方)在我们的 Web 服务器上跑了个"被攻陷"的模拟:恶意代码拿到了 Web 进程的执行权限,要往外偷数据。

按套路,它应该直接连攻击者的 IP。但蓝队聪明——他们知道防火墙会拦陌生外联。

于是恶意代码换了个路数:它不直连外网,而是把偷到的数据,通过 loopback(127.0.0.1:9099)发给了同一台机器上的另一个进程——一个"监控代理"。这个代理本来就有合法的出网 HTTPS 权限,它再把数据转发出去。

看明白了吗?从防火墙的视角,出网的流量来自"监控代理",是一个早就白名单放行的正常进程。** perimeter 上干干净净。**

在 6 小时里,Web 进程通过 loopback 往那个代理里传输了 2.1 GB 数据。代理的出网流量比平时暴涨 40 倍。

这段隐蔽信道,在边界安全设备眼里,根本不存在。


02 为什么防火墙看不见

核心原因一句话:边界安全看的是"南北向"流量,而隐蔽信道往往藏在"东西向"甚至"主机内"。

拆开看有三层盲区:

第一层:loopback 流量不走网卡。 127.0.0.1 上的通信,数据在内核协议栈里转一圈就结束了,从来不碰物理网卡。流量镜像、网桥、TAP——全抓不到。

第二层:Unix 域套接字根本不是"网络"。 两个进程用 /run/agent.sock 这种 Unix socket 通信,连 IP 都没有,纯内核里的文件描述符传递。任何网络设备都看不见。

第三层:合法进程做"骡子"。 攻击者把数据塞给一个本来就该出网的进程,让它的出网看起来"合理"。防火墙基于"哪个 IP 在通信"做判断,但决定数据正不正常的,是进程关系,不是 IP。

所以传统方案输在了一个根本点上:它知道 IP 在说话,却不知道是哪个进程在说话,更不知道这两个进程该不该说话。


03 eBPF 的网络可见性:远不止 XDP

说到 eBPF 做网络,很多人会想到 XDP——在网卡驱动层拦包。但 XDP 只看得见真正进出网卡的数据包,**loopback 和 Unix socket 照样看不见。**还是存在一些不足。

要抓主机内和进程级的通信,关键是这几类钩子:

socket 生命周期钩子(sock_ops)

挂在 cgroup 上,每次 socket 建立/关闭都会触发。能拿到四元组(源/目的 IP、端口)、协议、以及——最重要——当前进程的 PID 和进程名

数据路径钩子(sk_msg / TC)

  • sk_msg:在 sendmsg 路径上触发,能读到消息内容、统计体积
  • TC(流量控制)钩子:挂在网卡和 loopback 上,能看经过的所有包,包括主机内通信

系统调用 tracepoint

sys_enter_connectsys_enter_sendtosys_enter_sendmsg——任何进程发起连接、发送数据,都会留下痕迹。Unix socket 的通信也能通过对应的 tracepoint 追踪。

这些钩子的共同点是:每一次通信,都带着"是谁发起的"这个进程身份。 这是 eBPF 做网络检测区别于传统方案的根本——它不是在看包,是在看"进程之间的对话"。


04 隐蔽信道到底长什么样

隐蔽信道的核心思想是:在被监控的环境里,用“看似正常”的方式,把数据从A点偷偷送到B点。攻击者永远在和防御者的监控规则做猫鼠游戏——把恶意流量伪装成合法流量、把网络行为伪装成本地行为、把显式通信伪装成隐式行为。

下面把常见的攻击者花样系统化归类,并补充原理、真实案例、典型特征和 eBPF 检测思路:

① Loopback 骡子型(最常见的“代理骡子”)

原理:恶意进程不直接出网,而是把数据丢给本机(127.0.0.1 或 ::1)的某个合法出网进程(浏览器、更新程序、监控 agent、云安全客户端等),由后者代为转发。

典型玩法

  • • 后门把 C2 指令通过 localhost:8080 发给一个被劫持或被利用的合法服务。
  • • 恶意扩展/插件通过 loopback 和主浏览器进程通信,再由浏览器出网。
  • • 容器逃逸后,用宿主机上的正常代理或 kubectl 等工具出网。

特征边界防火墙/EDR 只看到“合法进程”对外通信,源 IP 是本机。 eBPF 检测:重点监控 connect()/send() 到 127.0.0.1 的调用,追踪发起者和最终出网进程的关联,构建“进程→本地 socket→出网进程”链路。

② Unix Socket 型(内核级隐身)

原理:完全绕过 TCP/IP 协议栈,使用文件系统上的 socket 文件(/var/run/xxx.sock 等)进行进程间通信。无 IP、无端口,网络监控设备完全看不见。

典型玩法

  • • Rootkit 或后门用 AF_UNIX socket 和一个合法 daemon 通信。
  • • Docker/containerd、systemd、DBus 等大量使用 Unix socket,攻击者很容易“搭车”。

特征:只出现在 /proc/<pid>/fd 中有 socket inode,路径通常在 /run/var/run/tmp 等目录。 eBPF 检测:trace unix_stream_connectunix_dgram_sendmsg 等 tracepoint,记录 socket 文件路径和通信双方进程,构建本地 IPC 图谱。

③ DNS / ICMP / 其他协议隧道型

原理:把数据编码进协议的“合法字段”中,伪装成正常流量。

细分

  • DNS 隧道:最流行。把数据 base32/ base64 编码后塞到子域名里(如 data1234567890.attacker.com)。特征是查询量大、子域名熵值高、TXT/NULL 记录异常
  • ICMP 隧道:把数据塞进 Echo Request/Reply 的 payload(ping 工具即可实现)。
  • HTTP(S) 隧道 / WebSocket / gRPC:伪装成正常 API 调用。
  • IPv6 隧道 / GRE / Teredo 等其他协议。

eBPF 检测:对 DNS 重点监控 dns_query(kprobe/uprobe),统计单位时间查询量、域名长度、熵值、唯一子域名数;对 ICMP 监控 payload 长度和规律性。

④ 共享内存 / 文件 / 管道型(纯本地隐蔽信道)

原理:完全不走网络,使用操作系统提供的共享资源传递数据。

常见形式

  • 共享内存shmget / mmap / memfd_create):两个进程映射同一块内存区域。
  • 文件:一个进程写临时文件,另一个进程读取(常配合 inotify 监听)。
  • Named Pipe / FIFO:经典的进程间管道。
  • Clipboard / Registry(Windows)Key-Value Store(Redis 本地实例) 等。

特征:syscall 频率高(read/write/mmapopen 同一文件),进程间无明显父子关系却频繁访问同一资源。 eBPF 检测:trace mmapshm*pipefs 相关 tracepoint,构建**“资源→使用进程”** 二部图,发现异常共享关系。

⑤ 时序 / 隐式信道型(最难检测)

原理不传输数据本身,只通过行为的时间、频率、顺序、资源占用等“副信道”编码信息

典型方式

  • • 进程 A 通过特定间隔的 CPU 占用 / 内存申请 / 文件访问,编码 0/1。
  • • 调节网络包发送间隔、TCP window size、packet size 等微小差异。
  • • Cache Side-Channel、Branch Prediction、Rowhammer 等更底层的硬件信道(APT 级别)。

特征:单看任何一个行为都很正常,只有统计学异常(互信息、熵、频谱分析)。 eBPF 检测:需要长时间收集 syscall / 网络包 / perf 事件,结合机器学习或统计模型发现“异常节律”。这也是目前 eBPF 最吃力的部分,常需用户态后处理。

所有隐蔽信道的共同破绽

无论哪一型,都逃不过两条核心线索:

  1. 1. 不该存在的通信关系:两个进程(或进程与资源)之间出现了不合理的关联(无父子、无同组、无共同服务却频繁交互)。
  2. 2. 异常的数据量或统计特征:短期突发高频、长期低频但持续、熵值异常、资源占用模式异常。

eBPF 的终极打法

  • • 构建全主机进程通信图(Process Communication Graph),节点是进程/容器,边是各种 IPC 方式(TCP/UDP/UnixSocket/SharedMem/File/Pipe/Signal 等),并给每条边打上“频次、数据量、熵、持续时间”等属性。
  • • 使用图算法找出低置信度边(即正常情况下很少出现的进程间关系)。
  • • 再叠加时序异常检测行为基线(每个进程的历史行为模型)。

这样就能把大部分隐蔽信道从“看不见”变成“在图上特别显眼”。


05 检测逻辑:画出进程的"社交关系图"

我们让 eBPF 把每一条 socket 事件(谁、连了谁、用什么、传了多少)报到用户态的 Rust agent。agent 干一件事:维护一张进程通信图。

代码语言:javascript
复制
基线(正常):
  web-server ──→ db:5432      (已知,放行)
  web-server ──→ redis:6379   (已知,放行)
  web-server ──→ payment:8443 (已知,放行)

异常(检测到):
  web-server ──→ 127.0.0.1:9099 (monitor-agent)  2.1GB/6h  ← 图上凭空多了一条边

判断逻辑不是"这个 IP 坏不坏",而是"这条边在基线里存在吗?数据量合理吗?"

那次对抗里,触发的规则是:

  • • web-server 进程向 127.0.0.1 发起连接 → 建立未知边
  • • 6 小时内传输 2.1 GB,远超 web-server 与任何本地进程的历史基线
  • • 对端 monitor-agent 同期出网涨 40 倍 → 两段行为在时间上强相关

三条叠加,直接红色。从第一条 loopback 连接建立到告警,< 1 秒。


06 Rust 实现:内核看见,用户态判断

和上一篇文件监控一样,这套组件也用 Rust + Aya 写,职责分离。

内核侧:用 sock_ops 抓连接关系

代码语言:javascript
复制
use aya_ebpf::programs::SockOps;
use aya_ebpf::macros::sock_ops;
use aya_ebpf::maps::PerfEventArray;

#[map]
static mut CONN_EVENTS: PerfEventArray<ConnEvent> =
    PerfEventArray::with_max_entries(1024, 0);

#[sock_ops]
fn on_sock_ops(ctx: SockOpsContext) -> u32 {
    let pid = unsafe { bpf_get_current_pid_tgid() >> 32 };
    let family = ctx.family();
    let daddr = ctx.remote_ip4();
    let dport = ctx.remote_port();
    // 连同进程身份一起,投递到用户态
    let ev = ConnEvent { pid, family, daddr, dport, .. };
    unsafe { CONN_EVENTS.output(&ctx, &ev, 0); }
    0
}

内核侧:用 TC 量体积、看隧道特征

代码语言:javascript
复制
#[tc]
fn on_egress(ctx: TcContext) -> i32 {
    // 解析包,统计体积;对 DNS 查询做子域名熵值估计
    // 异常高熵 + 高频 → 标记可疑
    0
}

用户态:构建图 + 跑异常检测

Rust agent 从 PerfEventArray 消费事件,维护进程通信图,基线学习 + 实时比对 + 规则引擎,命中就 kill + 隔离 + 告警三连,和文件监控篇的处置完全一致。

而且——这两套钩子(文件 + 网络)跑在同一个 eBPF agent 里。 文件监控看"进程动了哪些文件",网络监控看"进程跟谁说了话",合起来才是一个进程完整的行为画像。


07 这套方案的价值

把价值对照传统方案看,最清楚:

① 看见东西向和主机内流量。 这是 NIDS 的永久盲区。loopback、Unix socket、共享内存,全在 eBPF 视野内。

② 进程级归因。 每个包、每条连接,都知道是哪个 PID、哪个进程、哪个容器(cgroup 归属)发出的。你看的不是 IP,是"谁"。

③ 容器 / 微服务天然友好。 现代部署里东西向流量占绝大多数。eBPF 挂在 cgroup 上,自动按容器粒度归因,微服务间的异常调用一眼可见。

④ 实时、内核级、低开销。 和文件监控同理:全量捕获不采样,CPU 占用个位数百分比,毫秒级响应。

⑤ 可演进。 检测基于"进程关系图 + 行为基线",不是 IP 黑名单。攻击者换个 IP、换个域名没用,只要他还得在主机上让两个进程对话,就暴露。


08 但它也不是万能

照例说清楚边界:

  • 加密流量的明文看不见。 TLS 包只能看到"谁跟谁通了多大",看不到内容。要深入 payload,得上 eBPF uprobe 挂到 crypto 库,或在入口做 TLS 终止——复杂度高一个量级。
  • 跨主机需要每台都部署。 eBPF 是主机内技术,分布式系统的全链路要每台 agent + 中心聚合。
  • 误报要调。 微服务本来就爱互相调用,基线学习初期容易误伤,需要一段观察期。
  • 同进程内线程间信道最难。 两个线程共享地址空间,连 syscall 都不走,eBPF 也只能靠时序统计碰运气。

但作为"最后一道、最贴近进程行为"的防线,它对隐蔽信道的覆盖率,是传统边界安全完全给不了的。


09 写在最后

回到我们这三条安全线:

内存安全(Rust)——守住代码自己不写出漏洞; 文件监控(eBPF)——守住落盘的数据不被勒索加密; 网络监控(eBPF)——守住进程之间不出现不该有的对话。

三层叠起来,逻辑是一条:攻击只要还跑在你的操作系统上,它就一定会留下系统调用的痕迹。而 eBPF,就是把这些痕迹变成你能看见的信号。

防火墙在边界,eBPF 在内核。一个看门,一个看心。

看见底层,才能守住底层。


补充建议(可用于实际防御):

  • • 重点监控 /tmp/dev/shm/run 下的高频文件和 socket。
  • • 对 loopback 流量做额外策略(进程白名单 + 流量内容采样)。
  • • DNS 实现 QPS + 域名熵 + 机器学习异常检测三层过滤。
  • • eBPF + 用户态 Graph Database(如 Nebula/Neo4j)做离线关联分析效果最佳。

到这儿,Rust + eBPF 安全三部曲就讲完了:内存安全 → 文件防勒索 → 网络异常检测。


文中技术原理均基于 Linux eBPF 真实能力(sock_ops、TC、tracepoint、cgroup 归因),Aya 为实际可用的纯 Rust eBPF 框架。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2026-07-15,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Rust火箭工坊 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 01 一个防火墙看不见的漏洞
  • 02 为什么防火墙看不见
  • 03 eBPF 的网络可见性:远不止 XDP
  • 04 隐蔽信道到底长什么样
    • ① Loopback 骡子型(最常见的“代理骡子”)
    • ② Unix Socket 型(内核级隐身)
    • ③ DNS / ICMP / 其他协议隧道型
    • ④ 共享内存 / 文件 / 管道型(纯本地隐蔽信道)
    • ⑤ 时序 / 隐式信道型(最难检测)
    • 所有隐蔽信道的共同破绽
  • 05 检测逻辑:画出进程的"社交关系图"
  • 06 Rust 实现:内核看见,用户态判断
  • 07 这套方案的价值
  • 08 但它也不是万能
  • 09 写在最后
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档