大家读完觉得有帮助记得关注和点赞摘要限制应用程序可用的系统调用可以缩小内核的攻击面并极大地减轻受损程序的影响。近期研究展示了生成系统调用过滤器的技术然而所有现有解决方案都需要修改内核或应用程序才能在运行时激活它们。这是侵入性的、容易出错且通常不切实际尤其是当代码由外部方维护时。本文提出了SysComb一种基于eBPF的新颖解决方案用于根据应用程序状态强制执行时间专门化的系统调用过滤器无需修改应用程序或内核代码从而解决了上述局限性。此外SysComb让开发者可以在两种不同的强制策略之间选择类似seccomp的策略确保在状态转换后不会获得新权限以及最小权限策略对每个状态应用最具限制性的过滤器。我们使用广泛使用的软件评估了SysComb展示了准确的状态感知系统调用过滤以及可与内置内核解决方案相比的开销证明了我们方法的实用性。关键词系统调用过滤时间专门化攻击面缩减eBPFSeccomp1 引言限制内核与用户态之间的接口对于维护操作系统完整性至关重要。这种方法有双重目的它通过拒绝访问潜在易受攻击的代码路径来减少内核的攻击面[21]包括更容易出现漏洞的新内核函数[37]并且它限制了受损应用程序的能力[46]。这种纵深防御实践的重要性体现在其在关键应用如Web浏览器、服务管理器和容器运行时中的广泛采用这些应用在执行前限制应用程序可用的功能例如[30, 44, 18]。在Linux系统中对可用系统调用或syscall的限制是通过seccomp[41]实现的它通过指定过滤器来定义允许的系统调用集。此解决方案的有效性严格取决于过滤器的粒度[23]。理想情况下应用程序应仅被授予其当前和未来操作所需的系统调用。这需要在可行时转换到更具限制性的过滤器甚至将应用程序功能分区到不同的线程每个线程由专用的、严格的过滤器限制。然而在实践中大多数生产应用程序在启动时采用过于宽松的过滤器并且很少在之后修改它[6]。这是由于多种原因例如1. 对被调用的系统调用了解有限2. 重构应用程序以支持多个过滤器所需的工作量以及3. 将快速开发和部署置于安全性之上。认识到这些挑战大量研究工作致力于使用静态和动态分析技术生成与seccomp兼容的过滤器[25, 24, 26, 9, 54, 47, 16]。一些有影响力的工作[36, 25, 47]更进一步为同一服务器应用程序生成两个系统调用过滤器一个用于初始化阶段另一个用于服务阶段表明服务器应用程序的安全性可以大大受益于不同系统调用集的强制执行。虽然这些工作为系统调用过滤器的时间专门化概念铺平了道路但在运行时有效且高效地激活这些过滤器仍然构成重大挑战。实际上现有解决方案表现出以下一个或多个局限性1. 它们需要应用程序修改以显式触发过滤器转换2. 它们需要维护具有特殊功能的内核的替代补丁版本或3. 它们将过滤器的强制委托给可信的用户空间组件这代表了额外的攻击面。在本文中我们提出了一种新颖的解决方案以自动识别并透明地激活基于应用程序执行状态的专门化系统调用过滤器。我们还细化了时间专门化的概念使得能够强制执行每线程系统调用过滤器。据我们所知这是首次在不需应用程序更改、内核补丁和委托给用户空间组件的情况下实现此结果的提议。我们的解决方案通过利用eBPF子系统在内核边界内操作。这使我们能够使用用户空间探针在运行时自动检测应用程序状态转换观察所有系统调用事件并强制执行安全决策。所提出的技术是灵活的并支持两种策略1. 类似seccomp将应用程序线程限制为渐进式更具限制性的过滤器以及2. 最小权限将每个应用程序状态与可能的最具限制性过滤器关联。此外SysComb在运行时自动验证应用程序状态转换使得能够早期检测和阻止意外的执行模式。我们在广泛使用的应用程序上评估SysComb。从安全角度来看我们证明我们的方法识别出细粒度的、状态感知的过滤器更有效地减轻内核漏洞利用并且与基于单一过滤器的解决方案相比阻止了更多的shellcode。从性能角度来看我们表明SysComb引入的开销与seccomp相当。2 背景本节概述了使用seccomp进行系统调用过滤并提供了关于eBPF的基本背景eBPF是SysComb使用的基础技术。SeccompSeccomp BPF[41]或简称为seccomp是一种Linux内核安全机制允许进程定义系统调用过滤器。该过滤器是一个无状态程序由内核在系统调用调用时评估主要用于实现允许列表仅当系统调用标识符偶尔还有其原始参数与指定规则匹配时才允许其执行。关键的是一旦设置了过滤器它将被所有子线程和进程继承并且只能被收紧不能被放松。要安装过滤器应用程序进程必须配置它然后将其加载到内核中。过滤器设置有两种选择1. 使用一系列Berkeley Packet Filter规则直接编写它然后调用seccomp系统调用来激活它或2. 使用像libseccomp这样的平台无关高级库。无论哪种情况都需要修改应用程序可能需要重大的架构重构[11]。为减少开发人员的工作量一些解决方案允许在应用程序启动前仅显式声明一次其可用的系统调用例如[30, 18, 53]。尽管实用但这种方法不可避免地降低了有效性因为复杂软件需要许多系统调用当所有这些调用对每个内部组件都可用时存在具体的安全风险。图1eBPF架构概述。图描述显示用户空间组件如eBPF感知应用和库通过bpf系统调用与内核中的eBPF虚拟机交互。内核包含eBPF验证器、JIT编译器和eBPF程序及映射。程序附加到各种钩子点如kprobes、uprobes、tracepoints。eBPF。eBPF[15]是一种旨在安全且动态扩展操作系统内核功能的技术无需修改源代码或加载新的内核模块。从根本上说它允许用户定义的eBPF程序在内核内的抽象虚拟机中执行。当执行流到达指定的钩子点时这些程序被触发钩子点包括预定义位置如网络套接字和内核跟踪点以及当使用kprobes和uprobes附加时的任意内核和用户空间函数这些在主流Linux发行版中默认启用。此设计允许非侵入式检查参数和返回值并且对于某些钩子点它还允许程序覆盖内核对事件的反应方式。为跨调用持久管理状态程序使用称为eBPF映射例如哈希表、数组的共享数据结构。重要的是为降低内核不稳定的风险每个程序在加载前都经过严格的验证过程确保基本安全属性如保证终止、防止越界内存访问以及遵守系统定义的资源限制。图1展示了eBPF架构突出显示了负责加载eBPF程序和映射并与它们交互的用户空间组件。如图所示支持eBPF的应用程序依赖用户空间库来抽象低级bpf系统调用的复杂性并使eBPF程序能够可靠地针对各种内核版本而无需重新编译。3 需求与现有解决方案在本节中我们简要阐述目标、澄清威胁模型并讨论现有研究工作强调其优势和局限性。3.1 目标O1细粒度时间过滤器。我们旨在引入针对每个应用程序线程的特定执行状态定制的系统调用过滤器从而在潜在危险情况下如处理第三方数据或代码时限制可用操作。O2无需应用程序更改。时间策略不得要求开发人员重构应用程序源代码来激活系统调用过滤器。这便于采用特别是当需要限制第三方代码时。O3无需内核更改。除非补丁被内核社区接受并合并到主线否则每当有内核更新时都必须重新应用并可能需要调整它们引入相当大的维护开销。此外此代码中的错误可能导致系统不稳定。O4无需用户空间委托。将安全决策委托给外部监视器通常会因频繁的上下文切换而增加显著延迟。此外它总是依赖可信的用户空间组件这扩展了攻击面。O5完全支持依赖关系。我们寻求设计一个完全支持动态库和外部二进制文件执行的解决方案。详细地说我们希望a避免过度近似并从过滤器中排除所有虽然存在于动态库中但应用程序实际上并未使用的系统调用以及b即使在动态库和外部二进制文件内部也保持管理时间过滤器的能力。O6同步强制。强制必须有效地在系统调用调用和执行之间进行干预在危险操作影响系统之前阻止禁止的系统调用。3.2 威胁模型我们认为内核固有可信但可能受漏洞影响而用户空间应用程序或其执行的特定关键状态可能是不可信的和/或可能容易受到代码执行攻击。示例包括处理用户请求的Web服务器或执行用户提供查询的DBMS。这些应用程序状态的识别取决于特定的应用程序逻辑因此是由开发人员提供的信息。与seccomp等效SysComb的目标是通过严格限制应用程序可用的允许系统调用来限制应用程序-内核接口有效降低潜在受损软件的能力并最小化内核的攻击面[37, 41]。我们强调系统调用过滤解决方案的有效性取决于其在应用程序和内核之间进行干预以阻止禁止系统调用的能力。在SysComb中安全检查是同步的且不可绕过的。3.3 现有解决方案我们系统地分析了现有的系统调用过滤解决方案指出它们为何不能满足我们所有的要求。表1总结了分析结果。用户空间系统调用过滤有几种方法可用于在用户空间过滤系统调用。例如LD_PRELOAD[42]是一个环境变量用于指定在任何其他库之前加载的自定义共享库然后可以实现其自己的函数和系统调用包装器版本。ptrace[43]系统调用允许父进程观察和控制另一个进程的执行。Syscall User Dispatch (SUD)[34]允许应用程序捕获其自身的系统调用。然而所有这些方法都有缺点LD_PRELOAD的范围仅限于动态库ptrace由于频繁的上下文切换而引入高开销[57]而SUD并非旨在作为安全机制因为它可以通过在系统调用调用前跳转到非受限内存区域来轻松绕过。另一研究方向是二进制重写技术。例如zpoline[56]重写每个syscall和sysenter指令以调用用户定义的代码bpftime[58]使用相同技术。尽管开销最小但此方法无法捕获所有系统调用因为它不支持动态库。为克服这些局限性lazypoline[32]提出使用SUD捕获应用程序发出的所有系统调用然后利用zpoline重写遇到的任何系统调用指令。然而由于基于SUD这种方法不适合实现安全覆盖。基于Seccomp的解决方案几项研究工作侧重于生成准确的系统调用过滤器然后依靠seccomp通过使用应用程序包装器或注入二进制代码来强制执行。许多提议为整个应用程序生成单个seccomp过滤器然后在启动时应用它。例如sysfilter[16]使用静态分析从二进制文件生成函数调用图然后使用所有可达系统调用派生出单个过滤器然而在处理动态库时它会过度近似降低准确性。为改进此方面Chestnut[9]采用两步方法首先静态派生出初步系统调用列表然后通过动态分析进行细化。为将方法扩展到容器Confine[24]应用动态分析来发现运行的二进制文件列表然后对每个文件执行静态分析以派生出系统调用过滤器。最后C2C[26]提出结合应用程序的配置选项静态分析其源代码以识别死代码并禁用所有不可达的系统调用此方法主要适用于应用程序去臃肿化依赖于配置且与其它方法类似在处理动态代码时准确性较低。由于其构建方式此类别中的所有提议都生成一个宽松的单一seccomp过滤器包含应用程序所需的每一个可能的系统调用因此无法支持时间策略。另一方面有趣的研究描述了基于应用程序状态生成更严格过滤器的新方法。Ghavamnia等人[25]TSP使用对其源代码的静态分析为服务器应用引入时间系统调用过滤器。然而此方法仅支持两个执行阶段初始化和服务并且需要修补的编译器来重新编译所有应用程序代码包括第三方库。SysPart[47]受此工作启发通过还考虑动态模块大幅改进了二进制应用程序过滤器的生成。尽管有改进这两项工作仍然依赖seccomp来安装过滤器因此继承了其所有局限性见第2节。表1基于目标的近期系统调用过滤提议比较。O1时间过滤器O2无需应用程序更改O3无需内核更改O4无需委托O5依赖支持O6同步提议O1O2O3O4O5O6LD_PRELOAD [42]✗✓✓✗✗✗ptrace [43]✗✓✓✗✓✓Syscall User Dispatch [34]✗✓✓✗✓✗zpoline [56]✗✓✓✗✗✗lazypoline [56]✗✓✓✗✓✗bpftime [58]✗✓✓✗✗✗sysfilter [16]✗✓✓✓✗✓TSP [25]✓✗✓✓✗✓Chestnut [9]✗✓✓✓✗✓C2C [26]✗✓✓✓✗✓SysPart [47]✓✗✓✓✓✓Confine [24]✗✓✓✓✗✓SysXCHG [20]✓✓✗✓✓✓Jia et al. [33]✓✓✗✓✓✓B-Side [54]✓✓✓✓✓✗∗ Optimus [55]✗✓✓✓✓✓KubeRosy [31]✗✓✓✓✓✓μPolicyCraft [8]✓✓✓✗✓✗GoLeash [10]✓✓✓✗✓✗Tetragon [12], KubeArmor [5]✗✓✓✓✓✓Tracee [7], Falco [51], SysDig [52]✗✓✓✗✓✗我们的解决方案✓✓✓✓✓✓✗ 不支持 ◐ 部分支持 ✓ 支持 ∗ 实现依赖需要内核更改的解决方案为克服seccomp的局限性几种方法建议更改内核实现。SysXCHG[20]允许程序在每次执行execve时更改其seccomp过滤器符合最小权限原则。然而它需要广泛修改seccomp继承策略并且必须将过滤器嵌入ELF文件。Jia等人[33]提出修改seccomp和eBPF子系统以实现可编程系统调用过滤从而启用时间策略的强制。然而所提出的补丁未能在Linux内核社区获得足够共识最终未被纳入主线内核。最后B-Side[54]专注于二进制文件的静态分析以相对较高的准确性检测状态和相关过滤器但不支持过滤器强制建议要么更改内核要么引入用户空间监视器。尽管内核修改开启了大量机会但也引入了显著缺点。未合并的更改需要在每次内核更新时持续应用和修订补丁导致相当大的维护成本负担。基于eBPF的解决方案一条新的研究路线关注容器化应用程序并使用eBPF改进系统调用过滤[55, 31, 8, 10, 48, 1]。Optimus[55]检测应用程序所需的系统调用列表而KubeRosy[31]持续强制执行开发者提供的seccomp过滤器。然而两者都不支持时间策略。μPolicyCraft[8]超越了这一限制允许通过用户空间监视器生成和强制时间策略。然而由于安全决策被推迟到监视器处理事件强制仅在潜在危险操作已执行后才发生。专注于Go程序GoLeash[10]生成并强制执行每包系统调用过滤器。尽管它支持时间专门化但它将强制委托给异步用户空间组件。如今许多云原生解决方案如Falco[51]、KubeArmor[5]、Sysdig[52]、Tetragon[12]和Tracee[7]使用eBPF来监控和限制工作负载。虽然KubeArmor和Tetragon提供内核内强制能力但其它关注事件检测将强制留给用户空间模块。它们都为整个容器配置单一过滤器不支持时间专门化并采用阻止列表策略这固有地不如允许列表方法稳健。例如预先存在的过滤器不会阻止新引入的系统调用。4 SysComb架构我们首先在第4.1节通过突出其特性来概述我们的解决方案然后在专门章节中详细描述每个组件。4.1 功能概述在本文中我们通过将每个应用程序线程或进程与一个执行状态关联来细化时间专门化概念该状态可以在运行时演变O1。确实我们确保当线程达到开发者指定的代码位置我们称之为转换点之一时转换到新状态。为检测它们我们在应用程序二进制文件包括外部依赖项O5中设置eBPF用户探针以触发eBPF程序的执行无需对应用程序源代码或二进制文件进行任何修改O2。这允许透明地跟踪每个应用程序线程的当前状态并在达到转换点时评估状态管理逻辑。图2状态图图描述一个简单的有向图显示状态之间的转换。节点为状态0、状态1、状态2、状态3边表示从状态0到状态1、状态1到状态2、状态2到状态3的转换。为了捕获系统调用事件并强制执行安全决策我们配置了系统调用处理程序上的eBPF内核探针和程序。在过滤识别期间在跟踪模式下此设置记录应用程序线程发出的所有系统调用并将它们与当前应用程序状态关联。在强制模式下它检测被当前过滤器拒绝的系统调用并触发强制动作即记录事件、返回权限拒绝错误或终止目标线程。所有这些操作在系统调用执行之前在内核空间无缝执行O4O6。不需要对内核或eBPF子系统进行修改因此我们的解决方案可随时部署在任何运行内核v6.6或更高版本的Linux系统上O3。图3a最小权限图3b反向传播图3c类似seccomp图描述三个维恩图或集合图显示每个状态允许的系统调用。图3a显示每个状态有自己独立的集合。图3b显示通过反向传播状态0的集合变为状态0∪状态1∪状态2∪状态3的并集。图3c显示状态0的集合最大状态1是子集状态2是子集状态3是子集。黄色区域和虚线箭头突出显示反向传播的效果。SysComb支持两种强制策略1.类似seccomp通过始终转换到更具限制性的过滤器来模拟seccomp安全模型但透明地进行以及2.最小权限将每个应用程序状态绑定到最小的系统调用集产生更细粒度的过滤器。在跟踪模式下前者利用我们称为基于流的系统调用反向传播的后处理过程确保由于状态转换不会获得新权限而后者则不会。然而两种策略都依赖状态管理逻辑来在有向图中记录应用程序的状态转换。这对于确保劫持程序控制流的攻击者不能滥用状态转换故意获取权限至关重要。图2和图3展示了一个简单的应用程序状态图及其相关的系统调用过滤器突出了基于流的系统调用反向传播的效果。4.2 应用程序状态管理应用程序跟踪为简化描述我们将监控组件称为跟踪器将任何被跟踪的应用程序线程或进程称为被跟踪者。跟踪器提供了一个接口用于开始跟踪已在主机上运行的被跟踪者以及新进程。现有线程使用要跟踪的线程组即用户、进程或线程ID来识别而新进程通过fork-exec模式和在应用程序二进制文件的ELF入口点附加的eBPF程序来跟踪。为区分被跟踪者和其他任务跟踪器将每个被跟踪者的身份和执行状态注释到名为tracee map的eBPF映射中。在跟踪器的生命周期内此映射通过新的插入和删除来更新。使用clone和fork系统调用创建的新被跟踪者继承父任务相同的属性而终止的被跟踪者被移除。为此跟踪器将eBPF程序分别附加到与sched_process_fork和sched_process_exit事件关联的内核原始跟踪点。转换检测为实现对多状态的支持我们首先期望开发者指定转换点。这些可以位于应用程序及其依赖项中并且可以是1. 符号即函数名称2. 符号的偏移量或3. 绝对地址。跟踪器依赖blazesym库[38]从ELF二进制文件的.symtab和.dynsym部分以及系统上可单独安装的调试符号包解析符号。然后跟踪器将名为application state map的eBPF映射加载到内核中该映射存储每个执行状态的系统调用过滤器和有效转换。此映射在跟踪模式下填充在强制模式下为只读。每当被跟踪者达到转换点时都会对此eBPF映射进行写入和读取。为实现此目的跟踪器将uprobe eBPF程序附加到所有应用程序转换点。由于转换点可能很多内核提供了创建可附加到多个uprobe的单个eBPF程序的方法以及为每个附加点指定称为cookie的整数的方法允许我们明确检测转换点从而知道它们通向的应用程序状态。这使得能够保持tracee map中的执行状态最新。此外通过跟踪状态和转换跟踪器能够在跟踪模式下构建应用程序状态图图4并在强制模式下验证转换图5。确实在强制期间任何与应用程序状态图不匹配的转换都被视为无效并报告为流完整性错误。这允许检测和防止那些通过改变易受攻击应用程序的控制流以到达比从当前状态可达的过滤器集合更宽松的过滤器集合的攻击。图4跟踪模式下的应用程序状态管理。SysComb通过在转换点附加eBPF uprobes来跟踪线程状态并构建应用程序状态图。图描述显示跟踪器进程、eBPF uprobes、应用程序线程和状态图。图5强制模式下的应用程序状态管理。在运行时SysComb根据跟踪期间构建的状态图验证转换。图描述显示跟踪器、eBPF uprobes、应用程序线程和状态图验证。无效转换被阻止。图6跟踪每个应用程序状态使用的系统调用。图描述显示eBPF程序在系统调用进入时触发从tracee map获取状态并更新application state map中的系统调用位图。分离当所有被跟踪者终止时跟踪器可以收集记录的信息如果在跟踪模式下然后卸载加载的eBPF映射和程序。为实现此功能跟踪器维护一个表示存活被跟踪者任务数量的原子计数器。每当创建新被跟踪者时计数器递增每当一个终止时递减。除了自然终止跟踪器的早期终止也受支持可以通过向跟踪器进程发送SIGINT信号即CtrlC或仅在跟踪情况下通过最大持续时间计时器到期。基于被跟踪者的类型即使用入口点启动 vs 先前运行的进程跟踪器的早期终止导致不同结果。在指定了应用程序入口点的情况下跟踪器通过发送SIGTERM信号停止所有关联的被跟踪者然后等待它们终止。这确保所有被跟踪者任务被正确终止并且它们终止过程中调用的所有系统调用都被捕获。另一方面当跟踪器监控已在主机上运行的进程时跟踪器停止跟踪它们而不中断其执行。4.3 系统调用过滤器的识别系统调用跟踪系统调用事件通过将eBPF程序附加到sys_enter内核原始跟踪点来捕获。如跟踪点格式所示这允许eBPF程序接收有关系统调用号及其参数的信息。因此对于被跟踪者任务eBPF程序从tracee map检索当前状态然后将其用作application state map的索引以检索关联的系统调用位图最后基于系统调用号设置相应的位见图6。目前跟踪器仅记录系统调用标识符。然而此方法可以扩展为也跟踪原始类型参数类似于seccomp即不进行指针解引用以避免TOCTTOU问题。过滤器集的后处理跟踪终止后跟踪器开始后处理记录的信息以生成过滤器集。首先检测未使用的转换点并提示开发者因为它们在定义过滤器集时没有用。然后如果开发者选择了类似seccomp的策略跟踪器执行额外步骤即基于流的系统调用反向传播。此过程确保在遵循有效的转换序列后应用程序与逐渐更具限制性的过滤器关联即永远不会获得新权限。实现通过以反向拓扑顺序检索强连通分量合并属于同一分量的所有系统调用集并将系统调用反向传播到源节点来考虑应用程序状态图中可能存在的循环见附录0.A中的算法1。随后通过修剪冗余状态来简化图这些状态具有与每个前置节点相同的系统调用集。这简化了应用程序状态图从而减小了application state map的大小和附加到应用程序二进制文件的uprobes数量。最后跟踪器将结果导出到JSON策略文件示例见代码清单报告有关1. 应用程序和入口点如果可用2. 策略类似seccomp vs 最小权限以及3. 应用程序状态列表包括状态标识符、转换点即路径、符号名称、偏移量或绝对地址、人类可读格式的允许系统调用以及可达状态的信息。值得注意的是当采用最小权限策略时不执行基于流的系统调用反向传播因此识别出的系统调用过滤器可以显著更具限制性。然而有效的状态转换可能导致激活更宽松的过滤器。虽然这可能被视为风险但我们提醒过滤器不是由应用程序本身设置的而是由可信的内核内eBPF程序根据安全策略设置的。此外即使受到控制流劫持攻击的应用程序也无法逃脱系统调用过滤器。确实实现的控制流完整性检查确保只能基于可达状态的序列来行使系统调用。4.4 系统调用过滤器的强制策略加载在强制模式下跟踪器期望输入声明过滤器的JSON策略文件例如。在解析和验证文件格式后其内容被加载到application state map中。注意虽然SysComb在跟踪模式下操作支持生成此类系统调用过滤器但它们也可以由其他工具生成甚至手动编写。强制动作基于选定的配置跟踪器可以以三种不同方式对策略违反做出反应1. 记录事件2. 注入并返回错误代码或3. 终止被跟踪者。这些是现有系统调用过滤解决方案如seccomp[41]实现的常见操作可以适应多种应用程序需求。例如通过日志记录可以跟踪违反事件而不改变应用程序流程这在原型设计或微调过滤器时是有用的功能。另一方面返回错误代码允许强制执行安全限制同时让应用程序从错误中恢复并继续执行。这对于需要高可用性的关键应用程序很方便。最后终止被跟踪者确保在检测到安全违反后终止。基于动作的附加基于开发者请求的动作负责执行安全强制的eBPF程序附加到不同的钩子点。当请求的动作是日志记录时eBPF程序直接附加到系统调用进入内核跟踪点即sys_enter。当选择的动作是返回错误代码或终止时过程更复杂需要更多解释。出于安全原因eBPF程序可以改变正常内核执行流的钩子点存在限制而跟踪点与日志记录动作一起使用受这些限制。幸运的是Linux内核允许注入错误并修改所有实现每个系统调用入口的函数的返回值。然后可以通过检查内核源代码特别是检查系统调用函数声明中ALLOW_ERROR_INJECTION宏的使用来评估系统调用的完整覆盖代码清单1和2。¹ 虽然这种方法确保所有系统调用都被覆盖但缺乏所有系统调用的单一钩子点意味着跟踪器需要处理大量钩子点目前约1000个不同函数。幸运的是内核允许通过创建单个eBPF程序以可扩展方式执行此操作该程序附加到多个kprobes再次使用整数cookie来区分钩子点在这种情况下该cookie以系统调用号为参数。为检索此号跟踪器基于1. 内核支持的错误注入函数列表以及2. 实现系统调用的函数与其系统调用号之间的关系构建映射。前者由内核通过sysfs提供sysfs是一个导出关于各种内核子系统信息的伪文件系统。因此用户空间进程可以在/sys/kernel/debug/tracing/events/error_injection读取完整的错误注入函数列表。后者在内核编译期间生成并且可以在Linux头文件中获得或者可以从内核代码仓库下载。独立于基于动作的附加跟踪器能够拦截所有系统调用。然而并非所有附加方法性能相同因为当使用原始跟踪点时跟踪器加载eBPF跟踪程序这是kprobes和tracepoints的更现代替代方案它依赖eBPF trampolines来减少延迟。图7时间专门化系统调用过滤器的强制。被状态图拒绝的任何系统调用都会导致eBPF程序执行强制动作例如当调用write时返回错误。[图描述显示应用程序线程调用系统调用eBPF程序检查状态图如果允许则继续执行如果拒绝则注入错误。]强制当调用系统调用时负责强制的eBPF程序被触发。首先它确定发出系统调用的任务是否是被跟踪者。如果是它从tracee map读取当前任务的应用程序状态然后检索编码与其关联的过滤器的位图。最后它使用系统调用标识符作为参数提供给eBPF程序或从bpf_get_attach_cookie辅助函数的调用中获取作为索引来测试系统调用位图。如果过滤器允许系统调用eBPF程序返回内核继续其正常执行路径。否则执行开发者选择的强制动作。该过程的简化可视化如图7所示。注意当向应用程序发送终止信号时内核继续执行系统调用直到达到信号处理安全点。另一方面当注入错误时系统调用入口函数被覆盖其代码根本不执行。此行为具有安全影响。确实虽然终止被跟踪者可能看起来有效但它并不阻止系统调用代码至少部分执行这可能导致不良副作用。因此在实现中我们选择在向被跟踪者发送终止信号时始终注入错误代码。这确保系统调用代码永远不会被执行并防止副作用。4.5 局限性绿色线程一些编程语言实现绿色线程即由运行时库或虚拟机管理的轻量级线程。绿色线程在上下文切换期间通常比操作系统线程产生更低开销因为操作不涉及内核。确实绿色线程通常多路复用到单个操作系统线程中。目前我们的提议为每个操作系统线程分配一个状态因此不能直接应用于绿色线程。然而可以通过将uprobes附加到负责其创建和多路复用的运行时库或虚拟机函数来支持绿色线程。解释型编程语言并非所有编程语言都在构建时编译为本地代码因为一些例如Java、JavaScript、Python要么被解释要么即时编译并在运行时内执行。SysComb与这些语言不直接兼容因为它依赖将用户探针附加到应用程序二进制文件以透明地转换执行状态。然而由于运行时本身始终以本地代码编写可以将用户探针附加到运行时函数并基于其参数执行状态转换。附加点的识别以及运行时与应用程序操作之间关系的评估不幸需要运行时内部知识。特权跟踪器与允许非特权用户收紧其过滤器的seccomp相反SysComb的一部分需要超级用户权限来加载eBPF程序和映射。重要的是要注意这仅适用于跟踪器并且在eBPF对象加载后不再需要超级用户权限并在应用程序执行前自动释放。5 讨论我们讨论了SysComb实现的潜在攻击和对策。篡改eBPF对象与seccomp保证系统调用过滤器持久直到进程终止相反我们方法的正确操作取决于eBPF程序和映射的完整性。攻击者可以尝试通过以下方式篡改过滤器1. 通过bpf系统调用直接更改这些eBPF对象或2. 通过强制终止持有其引用的跟踪器进程来间接触发其移除。² 为解释可能的防御我们区分非特权攻击者和特权攻击者。非特权攻击者不能直接使用eBPF子系统因此只能进行间接尝试。为阻止其尝试SysComb将所有eBPF对象固定到eBPF文件系统bpffs[19]。相反为防止特权攻击者的直接更改我们通过Linux安全模块LSM配置额外的安全机制。确实LSM适用于非特权和特权进程并可以通过强制访问控制策略限制对内核资源包括eBPF对象的访问。目前除了常见的LSM如SELinux[49]和AppArmor[40]几项近期工作[5, 3, 2, 4]利用eBPF程序[22]来实现临时强制访问控制解决方案。控制流劫持攻击虽然seccomp和SysComb都通过限制允许的系统调用来减少受损应用程序的影响但两者都不能防止利用其控制流的用户空间漏洞。在SysComb中这可能导致跳转到任意转换点意图安装更宽松的过滤器。为规避此威胁在强制模式下我们仅允许在应用程序状态图中定义的状态转换。因此攻击者无法获得从当前状态不可达的系统调用。此外虽然不是我们提议的主要目标但它启用了控制流完整性违反的粗粒度检测并触发强制动作。6 有效性评估为评估我们解决方案的有效性我们1. 验证了跟踪应用程序调用的所有系统调用的能力2. 评估了服务器应用程序生命周期中过滤器大小如何演变最后3. 通过分析阻止近期内核漏洞的能力展示了安全优势。6.1 准确的系统调用检测我们通过使用strace[50]和SysComb跟踪常见服务器应用程序的执行来评估系统调用检测的准确性。具体来说我们通过运行各自的测试套件测试了Apache Httpd、Lighttpd、Nginx、Memcached、MongoDB、Redis、MySQL、PostgreSQL和Bind。³ 然后我们测量了过滤器大小定义为允许的系统调用数量。结果如图8所示。乍一看strace和SysComb无专门化SC_mono之间的比较可能表明SysComb遗漏了strace捕获的一些系统调用。然而进一步分析显示这种差异源于strace的特定操作行为它在调用启动被监控应用程序所需的execve之前开始跟踪从而捕获了涉及加载和初始化二进制文件的所有系统调用包括libc在__libc_start_main内部调用的那些。即使应用程序逻辑不需要这些系统调用它们也会被记录导致过于宽松的过滤器。相比之下SysComb从应用程序入口点开始监控。这产生了更严格的系统调用过滤器并与现有沙箱工具如Minijail[29]一致后者在执行__libc_start_main之后激活沙箱[28]。为验证这一点我们在Minijail沙箱中使用SysComb识别的过滤器重新运行了应用程序。如预期所有应用程序都成功完成了测试套件而无错误。图8aApache Httpd图8bLighttpd图8cNginx图8dMemcached图8eMongoDB图8fRedis图8gMySQL图8hPostgreSQL图8iBind图8描述九个条形图每个应用程序一个。显示strace、SysComb无专门化SC_mono、类似seccompSC_sec和最小权限SC_lp策略下每个状态Startup、Listen、Serve、Cleanup允许的系统调用数量。SC_sec和SC_lp的条形图通常低于SC_mono。6.2 细粒度专门化过滤器为评估状态感知过滤的优势我们比较了SysComb在有和没有时间专门化的情况下生成的系统调用过滤器。我们的目标是确定纳入执行上下文是否会产生更具限制性的策略。对于第6.1节提到的每个应用程序我们识别了三个关键转换点将任务执行划分为对应于主要服务器执行阶段的四个状态启动、监听、服务和清理。这些状态捕获i接受请求前的初始化ii等待客户端连接请求iii服务特定请求以及iv终止。图8总结了结果。总体而言状态感知过滤始终产生更具限制性的策略。确实虽然类似seccomp策略SC_sec最初应用的过滤器与无专门化情况SC_mono相同但它逐步转换到更严格的过滤器在所有应用程序中监听阶段平均减少11.82%的允许系统调用服务阶段减少20.77%清理阶段减少76.34%。最小权限策略SC_lp相比SC_mono在启动阶段不需要包含后续状态所需的系统调用进一步减少35.22%的系统调用监听阶段减少47.97%服务阶段减少22.05%清理阶段再次减少76.34%。通过比较图8中的SC_sec和SC_lp也可以看出不同状态除最后一个清理阶段外依赖不同的系统调用子集突出了最小过滤器的优势。表2阻止的安全敏感系统调用。应用程序acceptaccept4bindchmodcloneconnectexecveexecveatforklistenmprotectptracerecvfromsetgidsetreuidsetuidsocketApache Httpd✓✓✓✓✓✓✓✓✓✓Lighttpd✓✓✓✓✓✓✓✓✓✓✓✓Nginx✓✓✓✓✓✓✓✓✓✓✓Memcached✓✓✓✓✓✓✓✓✓✓✓✓✓MongoDB✓✓✓✓✓✓✓✓✓✓✓✓✓✓Redis✓✓✓✓MySQL✓✓✓✓✓✓✓✓✓✓PostgreSQL✓✓✓✓✓✓✓✓✓✓✓Bind✓✓✓✓被 ✓✓ 有专门化过滤 ✓ 无专门化过滤 未被任何方法过滤图9在总共535个shellcode中被阻止的shellcode数量。图描述条形图显示每个应用程序在无专门化和有专门化类似seccomp情况下被阻止的shellcode数量。有专门化时阻止的shellcode始终多于无专门化。6.3 攻击面缩减为展示攻击面缩减我们专注于阻止内核漏洞的能力。为此我们首先选择了45个近期内核CVE样本如附录0.B表5所示这些漏洞通过特定系统调用被利用。然后我们重新检查了第6.1节讨论的应用程序并计算了在以下情况下有多少应用程序受到保护1. 使用单一过滤器由seccomp或SysComb无专门化设置以及2. 为服务状态使用专门化过滤器。数据显示专门化过滤器在45种情况中有17种37.8%的CVE导致更好的保护。这一结果源于更有效过滤安全敏感系统调用的能力在[25]中定义为shellcode中经常利用的那些。确实在9个测试应用程序中表2报告的17个安全敏感系统调用在55.9%的情况下被SysComb无专门化阻止而当使用专门化时被阻止的比例为72.8%16.9%。参考先前研究[25, 47]识别的535个shellcode集这转化为每个应用程序平均多阻止98个shellcode27.8%如图9所示。我们提醒虽然SysComb提供了识别和强制专门化过滤器的独特解决方案但它也可以强制由其他方法[25, 47, 16]生成的过滤器。此外它消除了为激活而注入seccomp调用的需要它可以与两个以上状态一起工作并且支持最小权限过滤器。不需要用户空间监视器和内核修改。7 性能评估为评估SysComb的性能我们采用了1. 微基准测试以测量绝对系统调用拦截开销以及2. 宏基准测试以评估对常见软件的影响。测试在配备Linux内核6.8.0、Threadripper 2950X CPU、128 GiB RAM和1 TiB SSD的服务器上运行。微基准测试我们比较了不同的系统调用拦截方法测量了getpid系统调用的执行时间该系统调用以其最小的内核执行路径而闻名因此是隔离开销的理想选择。为提高准确性我们采用了高分辨率计时器执行了100万次连续的getpid调用最后计算了平均值。测试针对ptrace[43]、LD_PRELOAD[13]、基于中断的插桩int3、SUD[34]、zpoline[56]、seccomp[41]和SysComb重复进行。结果如表3所示。表3使用不同拦截解决方案在100万次调用上getpid的平均执行时间。拦截机制跟踪 (ns)强制 (ns)ptrace1193112019LD_PRELOAD200201int328803157SUD26582731zpoline198199seccomp5167217SysComb原始跟踪点297303SysCombKprobesn.a.487原生执行无拦截的平均执行时间为166 ns如预期所有拦截技术都比原生执行引入了一些开销原生执行平均耗时166 ns。需要上下文切换到用户空间监视器的解决方案ptrace、基于中断的插桩、SUD和用户空间通知模式下的seccomp引入了最高的开销每个系统调用的平均执行时间从2658 ns到12019 ns不等减慢一到两个数量级。相反完全在用户空间LD_PRELOAD、zpoline或完全在内核空间强制模式下的seccomp、SysComb操作的技术增加了较低的开销范围从198 ns到487 ns。尽管性能优越但用户空间解决方案容易被绕过见第3.3节而基于内核的解决方案提供了强大的保护。总体而言SysComb表现出有竞争力的性能在跟踪模式下平均执行时间为297 ns使用原始跟踪点的强制模式下为303 ns使用kprobes的强制模式下为487 ns。宏基准测试在真实场景中应用程序不会将大部分生命周期花在调用系统调用上因此拦截开销被用户空间和内核空间执行时间摊销。为评估这一点我们运行了几个宏基准测试测量了原生执行时间基线以及seccomp和SysComb拦截下的执行时间。测试的应用程序包括Apache Httpd、Lighttpd、Nginx、Memcached、MongoDB、Redis、MySQL、PostgreSQL和Bind。对于基准测试我们使用了1. Wrk[27]发送HTTP请求2. YCSB[14]提交键值读取和写入查询3. SysBench[35]和pgbench[45]执行SQL查询以及4. dnsperf[17]解析DNS名称。表4显示了请求延迟的第90百分位数。总体而言在跟踪模式下与基线相比SysComb增加了最少0.002毫秒Memcached和最多14.596毫秒Bind后者仅占Bind请求时间的4.46%。在强制模式下与seccomp相比SysComb的开销范围从0.000毫秒使用原始跟踪点的MySQL到2.233毫秒使用kprobes的Bind后者仅占Bind请求时间的0.68%。结果表明开销低表明我们的方法在生产环境中的可行性。最后我们强调强制动作的影响。当注入错误并返回给应用程序时开销稍高。这是由于使用kprobes而非原始跟踪点需要不同的eBPF程序类型并进行专门的安全检查以及检索系统调用号的额外步骤例如基于cookie的查找。然而影响仍然有限测量的最大延迟为0.521毫秒MySQL。表4使用不同拦截技术的第90百分位响应时间。所有值以毫秒为单位。应用程序基线seccompSysComb (跟踪)SysComb (强制)原始T.KprobesApache Httpd7.7317.7438.3178.3278.531Lighttpd11.44212.57812.63112.79012.852Nginx7.4077.4148.1388.1698.411Memcached0.0750.0770.0770.0780.080MongoDB0.1630.1660.1690.1700.169Redis0.1040.1060.1080.1090.108MySQL27.16528.67328.67328.67329.194PostgreSQL5.0345.1015.1115.1415.107Bind312.776326.209327.372328.116328.4428 结论我们提出了SysComb一种支持激活专门化系统调用过滤器的新颖解决方案。与现有工作不同SysComb不需要修改应用程序和内核代码也不引入用户空间监视器。确实通过利用eBPFSysComb完全在内核内操作以自动跟踪应用程序行为并实现对任何应用程序状态的每线程系统调用过滤器的完全透明、同步、不可绕过的强制。我们在九个广泛部署的应用程序上实现并评估了SysComb包括Web服务器、关系型和NoSQL DBMS以及DNS解析器。结果表明系统调用检测准确过滤器选择性提高从而增强了对内核漏洞和利用载荷的保护。标准基准测试表明性能与seccomp相当。总体而言SysComb提供了一种有效、低开销的解决方案减少了内核攻击面并稳健地缓解了现实世界应用程序中的攻击。
阅读完成 · 觉得有帮助?