组会纪要

2022-09-23 组会纪要

Posted by 李硕 on September 23, 2022

Jenny: Securing Syscalls for PKU-based Memory Isolation Systems

论文动机

  1. 保护系统调用很重要,但是方法需要“升级”
    1. 一般方法在内核中运行系统调用过滤器
    2. 不安全
    3. 限于进程层面
    4. 更细粒度的进程内沙箱逐渐流行
  2. 基于PKU的进程内沙箱技术
    1. ERIM、Hodor有缺陷
    2. Donky只拦截内存相关的syscall
    3. SealPK没有考虑系统调用的安全
    4. 嵌套域的处理、signal信号的安全处理等挑战

论文贡献

  1. 发现了新的针对基于PKU的隔离系统的系统调用攻击

  2. 设计了保护基于PKU的隔离域的系统调用过滤规则

  3. 进行了各种系统调用介入(interposition)技术对PKU的适用性比较研究,并提出了一种为PKU定制的新方案

  4. 设计了Jenny——第一个全面的基于PKU的隔离系统,支持安全的(同线程)用户空间系统调用过滤,安全的(异步)信号处理,以及x86-64下的安全多域PKU call gate

  5. 在不同的系统调用介入技术和过滤规则下对Jenny进行了原型实现和评估,并且代码开源

背景知识

  1. 系统调用过滤

约束进程,阻止其对某些系统调用和内核资源的访问;可限制暴露潜在的内核漏洞,减小攻击面

  • ptrace:允许拦截被跟踪进程(tracee)系统调用的入/出口

  • seccomp-bpf:seccomp为内核机制,能将进程可用的系统调用限制到最低限度。seccomp-bpf允许用户使用可配置的策略过滤系统调用,可以根据系统调用编号及其参数进行过滤
    • SECCOMP_RET_TRACE:通知基于ptrace的跟踪进程,”seccomp-ptrace”
    • SECCOMP_RET_TRAP:向调用者发送signal,”seccomp-trap”
    • SECCOMP_RET_USER_NOTIF:让用户空间程序做决策,”seccomp-user”
  • Syscall User Dispatch:新的系统调用仿真机制
    • 某个内存区域内的系统调用会正常执行
    • 区域外部的系统调用交给用户空间的信号handler处理
  1. PKU技术
  • Protection Keys for User-Space(a.k.a. MPK)

img

  • 32-bit的PKRU寄存器(Access/Write Disable)

  • WRPKRU/RDPKRU、XRSTOR

img

img

  1. 沙箱技术
  • 一种安全机制,为执行中的程序提供的隔离环境,通常会严格控制其中的程序所能访问的资源

  • 可用于限制潜在的恶意代码或有漏洞的代码影响系统的其余部分

  • 存在不同形式的沙箱(例如,虚拟机、容器、进程等),但这篇文章考虑基于PKU的进程内沙箱

  • 与其他进程内隔离技术类似,基于PKU的沙箱也必须过滤系统调用接口,避免逃逸

  1. PKU沙箱技术

基于Intel MPK的PKU沙箱的著名例子有ERIM、Hodor和Donky

  • ERIM:将MPK与二进制检查相结合,在高频率域切换时也具有较低的运行时开销

  • Hodor:检查可执行区域(加载时、权限变化时)、寻找WRPKRU指令、硬件断点指向指令序列以监视

  • Donky:基于动态内存保护域的强进程内隔离的高效软硬件协同设计方法,包括一个安全的软件框架和一个非侵入式的硬件扩展

本文工作中,作者主要关注Donky,Jenny构建在其软件框架之上。Donky是一个面向RISC-V和x86架构的PKU系统。针对RISC-V,它提出ISA修改,将违反保护密钥的行为转发给进程内监视器。当进入和退出监视器时,通过翻转策略寄存器中的位,Donky确保只有监视器可以更改寄存器。因此,不需要二进制扫描或W⊕X。Donky软件也可以与英特尔MPK一起使用,但只能以不安全的方式使用,因为它不能保护PKRU。

为什么需要PKU-aware的系统调用过滤

  1. 针对PKU沙箱有各种各样的攻击:
    1. 一些系统调用允许绕过PKRU权限访问任意内存(例如,process_vm_writev、ptrace)。类似地,/proc/self/mem处的procfs虚拟文件允许通过文件操作被不受限制地访问
    2. 只读内存可以改变(例如文件背景页),攻击者可以通过写入文件来生成不安全的WRPKRU指令
    3. 不可信域可以安装seccomp过滤器来操纵PKU相关系统调用,如pkey_mprotect等
    4. 不可信域可以注册信号处理程序并使用sigreturn系统调用将PKRU提升到任意权限
  2. 本文面临的挑战
    1. 如何获取PKU安全相关的系统调用过滤规则?
      • 分析系统调用接口
      • 定义PKU保护的资源,推导过滤规则
    2. 对于PKU沙盒,现有的系统调用介入技术有何限制,如何克服?
      • 分析不同的系统调用介入技术
      • 设计了“pku-user-delefate”
    3. 对于PKU沙盒,能否实现有效而全面的系统调用过滤?
      • 灵活定义过滤规则
      • 感知不同的PKU域
      • 过滤规则的嵌套
    4. 如何安全地结合沙箱(PKU感知)和系统调用过滤(能处理信号)?
      • 独立的栈保存信号帧
      • 虚拟化子域处理
      • 安全的信号处理handler
  3. 威胁模型
  • 非特权用户进程可以在沙盒中运行不可信的代码(例如插件),并对其进行防护

  • 对沙箱代码没有任何假设——它可能包含可利用的漏洞,也可能是完全恶意的,能执行攻击者提供的任意代码

  • 可信计算基由沙盒代码中的可信组件(PKU监视器、启动代码和链接器)、内核、一个内核模块和硬件组成,假设这些部分的实现是正确的,并且没有可利用的漏洞

  • 硬件缺陷、旁路和故障攻击都不在讨论范围之内。作者使用Donky框架作为基础,用星号“*”标记任何依赖于Donky提出的硬件更改的过滤器和机制

  1. 新的基于syscall的攻击

并非所有系统调用都会在访问用户空间内存之前检查PKRU寄存器。作者通过检查系统调用文档,作者确定了一些先前未知的、不同资源类别(即内存、进程、文件)的可利用系统调用:

  • 内存相关
  • madvise:无视保护键地清理页面
  • brk、sbrk:清理页面并移除保护键
  • userfaultfd:向不在内存的被保护页面任意写
  • 进程相关
  • (v)fork、clone
  • exec-like
  • arch_prctl、set_thread_area
  • personality(READ_IMPLIES_EXEC)
  • 文件相关
  • process_vm_writev
  • core dump:获取敏感的内存、寄存器状态等信息
  1. 系统调用过滤规则
    1. base-mpk

    额外定义针对mmap(2), (pkey_)mprotect, mremap, munmap, madvise的系统调用过滤规则

    磁盘映射安全:拦截mmap、mprotect,禁止共享的可执行映射

    确保可执行内存不是由可写文件支持的——如果用户可以修改文件就禁止映射

    core dump和procfs——使用内核特性prctl(PR_SET_DUMPABLE, SUID_DUMP_DISABLE),禁用core dump和对procfs的访问

    PKRU寄存器:扫描不安全的指令;对内存映射等实施W⊕X策略;拦截pkey_alloc与pkey_free以保护pkey

    过滤fork、clone、signal、sigaltstack等相关系统调用 ,阻断能修改整个进程行为的系统调用

    1. localstorage

    domain可能访问敏感的资源,如数据库、私钥等,需对其他domain屏蔽

    过滤基于路径和基于文件描述符的系统调用

    为每个domain创建本地存储目录,基于路径的系统调用只能访问域自己的本地存储,会透明地修改系统调用中的路径参数,如/tmp/file会被转换为/tmp/localstorage[PID]/[domain id]/tmp/file

    domain只能使用它们可以访问的文件描述符

    1. base-donky*

    过滤器与base-mpk相同,但不实施W⊕X策略

    只保护monitor代码和file-backing

    无二进制扫描,需要应用Donky提出的硬件修改来保护PKRU寄存器

系统调用过滤机制

  1. 现有机制对比

无法感知PKU域、依赖不安全的信号处理、过滤器不够灵活、速度慢、x86-64不可用

img

  1. pku-user-delegate
  • 内核模块

  • 选择性地拦截系统调用
    • thread_info结构体设置bit位
    • 拦截监视器注册的系统调用
    • 非监视器域系统调用
  • 用户空间监视器进行过滤
    • 减小内核攻击面
    • 过滤器代码更灵活
    • 过滤规则易拓展

Jenny-针对PKU的安全高效系统调用过滤

第一个全面的基于PKU的隔离系统,支持安全高效的系统调用过滤、信号处理和多域间的PKU call gate;基于PKU框架Donky并拓展,为注册、调用系统调用过滤器和信号处理handler提供了必要的逻辑。过滤器是PKU域感知的,允许跨域的分层系统调用过滤。Jenny为用户定义的系统调用过滤器提供了一个通用的编程接口,支持不同的过滤机制;为了适用原生x86-64 CPU,实现了W⊕X策略、二进制扫描和安全call gate。最后,进行安全性论证并设计了PKU安全信号处理方案。

  1. 设计

img

  1. 同线程嵌套过滤

img

img

  1. 过滤器设计

系统调用参数在过滤过程中可能改变

  • 拷贝系统调用中的敏感指针参数——防止TOCTOU攻击

  • PKRU寄存器检查

  • 数据结构上锁

    • 每个数据结构必须只被一个域(例如根域)锁定和访问
    • 为了避免通过递归产生死锁,过滤器代码本身不能执行任何显式的域切换
  1. 多域call gate

call gate是进入PKU域的安全入口点

  • 传统的call gate
    • 防止攻击者恶意重用WRPKRU指令
    • double-check %rax寄存器
    • 只支持静态的$PKRUVALUE
  • 通用的多域call gate
    • PKRU值从线程本地存储获取
    • img
  1. 信号处理
  • 安全的信号API:监视器仅允许一个信号由一个域处理。此外,父域可以覆盖由其子域注册的信号处理handler,从而对其虚拟化子域。父级可以决定是否将信号重定向到子域

  • 安全的信号stack:为每个线程分配独立的信号堆栈,并用pkey保护,两者同时注册到内核(拓展了sigaltstack系统调用)

  • 安全的信号handler:消除竞态条件

    • 暂时阻塞所有的信号
    • 防止信号干扰当前的监视器执行,将信号处理推迟到监视器退出点
    • 避免信息泄露给信号handler,只将signal帧放在受保护的signal栈上,不提供给域的信号handler
  1. 安全论证
    1. 系统调用介入技术
    2. 无法抵御execve攻击——拦截execve系统调用
    3. 可通过ioctl重新配置——初始化之后再拦截ioctl
    4. 基本过滤器
    5. 不能保证阻止所有的系统调用攻击——默认拦截新的系统调用
    6. 使用strace对系统调用分类,再手工分析
    7. Jenny设计:确保域都会进行系统调用过滤
    8. 应用过滤规则:对系统调用参数进行“清理”

评估

  1. Micro-benchmarking

img

img

  1. Application Benchmarks

img

  1. Case study:nginx webserver

img

img

讨论

  1. 兼容性问题
    1. 系统调用限制
    2. localstorage限制
    3. 不安全的WRPKRU (以及 XRSTOR )指令
  2. 内核接口
    1. Linux内核的系统调用接口数理与复杂程度都在增加
    2. 建议只允许必要的系统调用,过滤未知的系统调用
  3. pkey数量
    1. Intel MPK只支持16个key
    2. 保护过滤的系统调用参数的策略,造成pkey大量使用,即sysargs key
    3. 解决方案是对sysargs key进行请求式分配

结论

  • 解决了基于PKU内存隔离系统的各种系统调用过滤挑战,从而实现了第一个具有全面系统调用支持的PKU内存隔离系统Jenny

  • 发现了新的与PKU相关的系统调用攻击,比较了各种系统调用拦截机制,并设计了一种更快的符合PKU系统需求的机制

  • 设计了全面高效的过滤规则以保护PKU沙箱

  • Jenny在同一个线程上提供过滤,支持嵌套的系统调用过滤和信号处理

  • 证明了Jenny的系统调用过滤既实用又安全,并且对nginx的性能影响只有5%

附:

  1. 信号处理过程

img

  1. “内核空间的PKRU”

Protection Keys for Supervisor(PKS):在内核中修改page protection的bit的话,操作会比较耗时,因为这里牵涉到了TLB invalidation等会影响系统性能的动作。因此频繁使用这个机制的话会损害kernel的性能。通常来说禁止kernel访问内存是用来预防kernel bug导致破坏的,而这种bug其实并不常见,因此多数人并不喜欢为了这个目的而损失性能。PKS是针对未来的Intel CPU所准备的功能。PKS会对kernel地址空间中的每个page都跟一个4-bit的protection key关联起来,这样每个page都是属于16个内存区域中的一个。每个内存区域都可以被独立地配置为禁止kernel的写操作,或者彻底禁止kernel的所有访问。修改这些限制策略会比直接修改这些page的protection bit设置要更加高效。

[PKS: Add Protection Keys Supervisor (PKS) support LWN.net]

  1. 特殊的系统调用绕过PKRU说明
  • madvise绕过PKRU限制:

flag

MADV_DONTNEED:在未来一段时间不会访问。成功执行MADV_DONTNEED操作后,将更改指定区域中的内存访问的语义:对该范围内页面的后续访问将成功,但是将导致从底层映射文件(共享文件映射,共享匿名映射和基于shmem的技术,例如System V共享内存段)的最新内容中重新填充内存或按需按零填充页面(私有匿名映射)。

MADV_FREE:该应用程序不再需要由addr和len指定的范围内的页面。内核可以释放这些页面,但是释放可以延迟到出现内存压力为止。对于已标记为将释放但尚未释放的每个页面,如果调用者写入该页面,则释放操作将被取消。成功执行MADV_FREE操作后,内核释放页面时,所有过时的数据(即脏的,未写入的页面)都将丢失。但是,随后对该范围内的页面的写操作将成功,然后内核无法释放那些脏页,从而使调用者始终只能看到已写的数据。如果没有后续写入,则内核可以随时释放页面。释放范围内的页面后,调用者将在随后的页面引用中看到按需填充零页面。MADV_FREE操作只能应用于私有匿名页面。

MADV_WIPEONFORK:在fork之后,在此范围内为子进程提供零填充内存。这对于服务器fork很有用,以确保不会将敏感的进程数据(例如PRNG种子,加密机密等)传递给子进程。该操作只能应用于私有匿名页面。

攻击者利用这个syscall去操控内核清理特定的内存页面,而这种行为是不受MPK/PKU控制的(因为MPK/PKU是在用户空间发挥限制的手段)。

  • brk,sbrk绕过PKRU限制:

brk,sbrk都是用来管理堆内存的。由于PKRU允许domain分配内存、保护私有内存,如果这些内存在堆上的话,恶意domain可以用brk,sbrk解除分配或者重新分配内存。

brk完成内存分配后,还是没有物理页与之对应的, 等到进程第一次读写这块内存的时候,发生缺页中断,内核才分配这块内存对应的物理页。这个过程是不涉及PKRU检查的。