基于eBPF的安全可信协议栈

今天为大家介绍清华大学徐恪老师团队提出的一个面向无状态协议攻击的防御系统——ICMP Filter,其能够以防御组件的形式热加载进Linux内核中,监测入站ICMP报文的频率和语义特性。系统在维持轻量化设计的同时,对潜在的恶意攻击报文进行提前过滤,并向用户及时反馈防御日志。目前的实现可以应用于Linux 6.4以上的版本中。

GitHub项目地址:

https://github.com/Internet-Architecture-and-Security/eBPF-based-Defense-Against-TCP-IP-Exploits-via-Forged-ICMP-Errors

01 研究背景:无状态协议的语义缺失问题

TCP/IP协议栈通过分层设计实现了网络互联的标准化与高效性,它定义了数据如何在网络中封装、寻址和传输,支撑了全球互联网Internet的互联互通。其中,无状态的UDP/ICMP等协议,凭借低开销、实时性强的特性,广泛应用于视频流传输、在线游戏、网络诊断(如ping/traceroute)等场景,灵活满足了不同应用对可靠性、速度及功能的需求,共同构建了现代网络通信的基石。

然而,由于UDP/ICMP的无状态设计特性,在当前广泛应用的Linux内核实现中并不会专门维护其对应的协议状态机,而对于这两种报文合法性的校验往往采用弱校验的方式,在弱校验下攻击者可以较低的成本轻易构造内嵌报文以欺骗内核协议栈,无状态协议的语义缺失无疑为攻击者带来了可乘之机。近年来,该问题也引起了学术界的关注,在国际四大安全会议上多个研究均利用此机制实现了对TCP连接的攻击甚至劫持。

本系统通过抓住这类问题的本质,从根源上对协议栈进行防护,并基于Linux内核的eBPF子系统进行实现,相比于Linux内核补丁或内核模块,具有更好的可移植性和鲁棒性。

02 基础技术:Linux内核利器eBPF介绍

eBPF是一种可以在特权级上下文(如操作系统内核)中运行用户自定义程序的技术,是对Linux内核中的数据包过滤技术BPF(Berkeley Packet Filter)的扩展,其能够在不改变内核源码或加载内核模块的前提下帮助用户扩展额外功能,如今已被实现在Linux主线中。

eBPF的核心实现是一个虚拟机,能够运行自定义的64位的RISC指令集。用户通过编写自定义的eBPF程序,通过编译器生成BPF字节码,随后加载进内核中的eBPF子系统执行JIT(Just-in-Time)编译和验证,并调用helper API与操作系统运行时进行交互。eBPF子系统中同时实现了eBPF Map,用户可以通过eBPF程序和系统调用两种方式访问eBPF Map,使用API对数据进行读写,以实现状态存储与通信需求。eBPF通过指定程序类型以将eBPF程序挂载至内核中预设的不同的钩子处,截至目前eBPF中负责协议栈入站数据包处理的程序类型主要有3类,xdp (Linux 4.8引入), tc (Linux 4.1引入)和netfilter (Linux 6.4引入)。

图1 Linux中的eBPF实现

03 系统设计:无状态协议攻击防御系统

团队提出的面向无状态协议攻击的防御系统主要分为两个部分,其一是内嵌无状态协议的ICMP Error报文过滤模块,其二是ICMP输入速率限制模块。防御机制以eBPF的netfilter类程序的方式实现,挂载在NF_INET_LOCAL_IN钩子处,该钩子位于Linux内核的IP数据包入站路径上,对于所有的入站数据包的处理,均会调用该eBPF程序进行额外的过滤和防御。

1.内嵌无状态协议的ICMP Error报文过滤模块

当入站数据包为ICMP Error报文时,此模块会被激活并尝试过滤具有威胁的数据包。RFC 792中规定,当触发ICMP Error报文时,需在ICMP的数据字段内嵌触发该Error报文的原IP报文的前28个字节。28个字节中包含了原报文状态相关的重要信息,如IP首部的协议字段,TCP/UDP的源目的端口号,TCP的序列号等。对于内嵌的原报文为有状态报文(TCP),在内核中已存在较强的校验机制,故不做处理;对于内嵌的原报文为无状态报文(UDP/ICMP),由于内核并未维护具体状态信息,故转而采取了弱校验,攻击者可以轻易地构造伪造报文以绕过。如果内嵌报文是被动产生的报文,如ICMP Error/ICMP Echo Reply,由于这类报文并不是由终端主动产生,理论上不应再次触发ICMP Error报文,故报文过滤模块对这类报文进行了过滤。特别地,对于ICMP Redirect报文,其主要用于通告终端到目的IP的新的网关选择,正常情况下只是为了通告较短的路径,就算按照原来的路径数据包也能顺利送达目的地,但是一旦被攻击者利用,就可以轻易达成对指定目的IP流量的DoS或者劫持攻击,故对于这类报文,一旦其中内嵌的原报文为无状态协议,那么报文过滤模块也会将其过滤。

图2 ICMP Filter在Linux内核中的执行流程图

2.ICMP输入速率限制模块

攻击者在尝试利用ICMP Error报文进行攻击时,暴力枚举可能的内部无状态报文是一种常见的方式,此外,当伪造的ICMP Error报文通过协议栈的校验后,攻击者仍会通过ICMP报文的反馈机制尝试观测侧信道。以上两种情况均会产生大量的ICMP报文,在目前版本的Linux内核实现中,只对ICMP报文的输出速率进行了限制,以防御DoS攻击,但是并未对输入速率进行额外限制。考虑到攻击者有可能会拥有一个IPv4网段,在本模块中,对来自每个/24网段的ICMP报文进行独立限速,为防止限速阈值限制产生侧信道,每秒钟随机接收500~1000个ICMP报文,具体实现通过对每个网段基于eBPF Map独立维护一个LRU credit表项完成。由于内核线程在处理数据包是并行的,为防止读写竞争,对于表项的修改应设计对应的同步互斥机制,一个比较简单的方式是使用自旋锁。然而,当攻击者构造大量ICMP报文时,使用自旋锁会带来头阻问题,在一定程度上甚至会达到DoS攻击的效果。对此,在此模块中,使用GCC提供的原子操作指令,通过“尝试-回退”的方式更新credit值,在避免读写竞争的同时近似逼近原本预期达到的效果。

3.防御日志监测模块

为了方便用户观测两个模块的防御记录,以及时做出应对,本系统中通过Ring Buffer类型的eBPF Map实现了日志系统,能够将内核中eBPF程序实际进行的防御操作及时反馈到用户空间,在日志信息中包含两个子模块具体进行的操作以及被过滤报文的源IP地址。

图3 ICMP Filter总体架构图

04 结语

ICMP Filter从无状态协议攻击的成因出发,利用Linux的eBPF子系统完整实现了对这类攻击依赖的主要报文的过滤和防御,并且设置了日志系统将防御记录第一时间反馈给用户。团队后续会继续对Linux内核协议栈漏洞的系统防御进行深入研究,并与业界和社区开展密切合作,将成果服务于社区生态。

敬请关注团队后续的更多研究成果发布!

声明:本文来自赛博新经济,稿件和图片版权均归原作者所有。所涉观点不代表东方安全立场,转载目的在于传递更多信息。如有侵权,请联系rhliu@skdlabs.com,我们将及时按原作者或权利人的意愿予以更正。

上一篇:因违反网络安全合规要求,这家公司被罚超1000万元

下一篇:Elastic Security 2024全球威胁报告概述