防火墙、NIDS、流量镜像,全在岗。 可一台服务器上的两个进程,悄无声息地传了 2.1 GB 数据出去。 perimeter 上干干净净,什么都没看到。 最后抓住它的,还是那段跑在内核里的eBPF代码。
先讲个红蓝对抗里的真事。
蓝队(防守方)在我们的 Web 服务器上跑了个"被攻陷"的模拟:恶意代码拿到了 Web 进程的执行权限,要往外偷数据。
按套路,它应该直接连攻击者的 IP。但蓝队聪明——他们知道防火墙会拦陌生外联。
于是恶意代码换了个路数:它不直连外网,而是把偷到的数据,通过 loopback(127.0.0.1:9099)发给了同一台机器上的另一个进程——一个"监控代理"。这个代理本来就有合法的出网 HTTPS 权限,它再把数据转发出去。
看明白了吗?从防火墙的视角,出网的流量来自"监控代理",是一个早就白名单放行的正常进程。** perimeter 上干干净净。**
在 6 小时里,Web 进程通过 loopback 往那个代理里传输了 2.1 GB 数据。代理的出网流量比平时暴涨 40 倍。
这段隐蔽信道,在边界安全设备眼里,根本不存在。
核心原因一句话:边界安全看的是"南北向"流量,而隐蔽信道往往藏在"东西向"甚至"主机内"。

拆开看有三层盲区:
第一层:loopback 流量不走网卡。 127.0.0.1 上的通信,数据在内核协议栈里转一圈就结束了,从来不碰物理网卡。流量镜像、网桥、TAP——全抓不到。
第二层:Unix 域套接字根本不是"网络"。 两个进程用 /run/agent.sock 这种 Unix socket 通信,连 IP 都没有,纯内核里的文件描述符传递。任何网络设备都看不见。
第三层:合法进程做"骡子"。 攻击者把数据塞给一个本来就该出网的进程,让它的出网看起来"合理"。防火墙基于"哪个 IP 在通信"做判断,但决定数据正不正常的,是进程关系,不是 IP。
所以传统方案输在了一个根本点上:它知道 IP 在说话,却不知道是哪个进程在说话,更不知道这两个进程该不该说话。
说到 eBPF 做网络,很多人会想到 XDP——在网卡驱动层拦包。但 XDP 只看得见真正进出网卡的数据包,**loopback 和 Unix socket 照样看不见。**还是存在一些不足。
要抓主机内和进程级的通信,关键是这几类钩子:
socket 生命周期钩子(sock_ops)
挂在 cgroup 上,每次 socket 建立/关闭都会触发。能拿到四元组(源/目的 IP、端口)、协议、以及——最重要——当前进程的 PID 和进程名。
数据路径钩子(sk_msg / TC)
sk_msg:在 sendmsg 路径上触发,能读到消息内容、统计体积TC(流量控制)钩子:挂在网卡和 loopback 上,能看经过的所有包,包括主机内通信系统调用 tracepoint
sys_enter_connect、sys_enter_sendto、sys_enter_sendmsg——任何进程发起连接、发送数据,都会留下痕迹。Unix socket 的通信也能通过对应的 tracepoint 追踪。
这些钩子的共同点是:每一次通信,都带着"是谁发起的"这个进程身份。 这是 eBPF 做网络检测区别于传统方案的根本——它不是在看包,是在看"进程之间的对话"。
隐蔽信道的核心思想是:在被监控的环境里,用“看似正常”的方式,把数据从A点偷偷送到B点。攻击者永远在和防御者的监控规则做猫鼠游戏——把恶意流量伪装成合法流量、把网络行为伪装成本地行为、把显式通信伪装成隐式行为。
下面把常见的攻击者花样系统化归类,并补充原理、真实案例、典型特征和 eBPF 检测思路:
原理:恶意进程不直接出网,而是把数据丢给本机(127.0.0.1 或 ::1)的某个合法出网进程(浏览器、更新程序、监控 agent、云安全客户端等),由后者代为转发。
典型玩法:
特征:边界防火墙/EDR 只看到“合法进程”对外通信,源 IP 是本机。
eBPF 检测:重点监控 connect()/send() 到 127.0.0.1 的调用,追踪发起者和最终出网进程的关联,构建“进程→本地 socket→出网进程”链路。
原理:完全绕过 TCP/IP 协议栈,使用文件系统上的 socket 文件(/var/run/xxx.sock 等)进行进程间通信。无 IP、无端口,网络监控设备完全看不见。
典型玩法:
AF_UNIX socket 和一个合法 daemon 通信。特征:只出现在 /proc/<pid>/fd 中有 socket inode,路径通常在 /run、/var/run、/tmp 等目录。
eBPF 检测:trace unix_stream_connect、unix_dgram_sendmsg 等 tracepoint,记录 socket 文件路径和通信双方进程,构建本地 IPC 图谱。
原理:把数据编码进协议的“合法字段”中,伪装成正常流量。
细分:
data1234567890.attacker.com)。特征是查询量大、子域名熵值高、TXT/NULL 记录异常。eBPF 检测:对 DNS 重点监控 dns_query(kprobe/uprobe),统计单位时间查询量、域名长度、熵值、唯一子域名数;对 ICMP 监控 payload 长度和规律性。
原理:完全不走网络,使用操作系统提供的共享资源传递数据。
常见形式:
shmget / mmap / memfd_create):两个进程映射同一块内存区域。特征:syscall 频率高(read/write/mmap、open 同一文件),进程间无明显父子关系却频繁访问同一资源。
eBPF 检测:trace mmap、shm*、pipe、fs 相关 tracepoint,构建**“资源→使用进程”** 二部图,发现异常共享关系。
原理:不传输数据本身,只通过行为的时间、频率、顺序、资源占用等“副信道”编码信息。
典型方式:
特征:单看任何一个行为都很正常,只有统计学异常(互信息、熵、频谱分析)。 eBPF 检测:需要长时间收集 syscall / 网络包 / perf 事件,结合机器学习或统计模型发现“异常节律”。这也是目前 eBPF 最吃力的部分,常需用户态后处理。
无论哪一型,都逃不过两条核心线索:
eBPF 的终极打法:
这样就能把大部分隐蔽信道从“看不见”变成“在图上特别显眼”。
我们让 eBPF 把每一条 socket 事件(谁、连了谁、用什么、传了多少)报到用户态的 Rust agent。agent 干一件事:维护一张进程通信图。
基线(正常):
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 坏不坏",而是"这条边在基线里存在吗?数据量合理吗?"
那次对抗里,触发的规则是:
三条叠加,直接红色。从第一条 loopback 连接建立到告警,< 1 秒。
和上一篇文件监控一样,这套组件也用 Rust + Aya 写,职责分离。
内核侧:用 sock_ops 抓连接关系
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 量体积、看隧道特征
#[tc]
fn on_egress(ctx: TcContext) -> i32 {
// 解析包,统计体积;对 DNS 查询做子域名熵值估计
// 异常高熵 + 高频 → 标记可疑
0
}用户态:构建图 + 跑异常检测
Rust agent 从 PerfEventArray 消费事件,维护进程通信图,基线学习 + 实时比对 + 规则引擎,命中就 kill + 隔离 + 告警三连,和文件监控篇的处置完全一致。
而且——这两套钩子(文件 + 网络)跑在同一个 eBPF agent 里。 文件监控看"进程动了哪些文件",网络监控看"进程跟谁说了话",合起来才是一个进程完整的行为画像。
把价值对照传统方案看,最清楚:
① 看见东西向和主机内流量。 这是 NIDS 的永久盲区。loopback、Unix socket、共享内存,全在 eBPF 视野内。
② 进程级归因。 每个包、每条连接,都知道是哪个 PID、哪个进程、哪个容器(cgroup 归属)发出的。你看的不是 IP,是"谁"。
③ 容器 / 微服务天然友好。 现代部署里东西向流量占绝大多数。eBPF 挂在 cgroup 上,自动按容器粒度归因,微服务间的异常调用一眼可见。
④ 实时、内核级、低开销。 和文件监控同理:全量捕获不采样,CPU 占用个位数百分比,毫秒级响应。
⑤ 可演进。 检测基于"进程关系图 + 行为基线",不是 IP 黑名单。攻击者换个 IP、换个域名没用,只要他还得在主机上让两个进程对话,就暴露。
照例说清楚边界:
但作为"最后一道、最贴近进程行为"的防线,它对隐蔽信道的覆盖率,是传统边界安全完全给不了的。
回到我们这三条安全线:
内存安全(Rust)——守住代码自己不写出漏洞; 文件监控(eBPF)——守住落盘的数据不被勒索加密; 网络监控(eBPF)——守住进程之间不出现不该有的对话。
三层叠起来,逻辑是一条:攻击只要还跑在你的操作系统上,它就一定会留下系统调用的痕迹。而 eBPF,就是把这些痕迹变成你能看见的信号。
防火墙在边界,eBPF 在内核。一个看门,一个看心。
看见底层,才能守住底层。
补充建议(可用于实际防御):
/tmp、/dev/shm、/run 下的高频文件和 socket。到这儿,Rust + eBPF 安全三部曲就讲完了:内存安全 → 文件防勒索 → 网络异常检测。
文中技术原理均基于 Linux eBPF 真实能力(sock_ops、TC、tracepoint、cgroup 归因),Aya 为实际可用的纯 Rust eBPF 框架。