STACKLEAK如何提高Linux内核安全性

STACKLEAK是Linux内核安全功能,最初由Grsecurity / PaX的创建者开发。 我决定将STACKLEAK引入正式的普通内核(Linux内核主线)。 本文将讨论此安全功能的内部结构,属性及其在主线中很长的困难之路。





STACKLEAK可以防止Linux内核中的几类漏洞,即:

  • 减少对攻击者有用的信息,这些信息可能会从核堆栈泄漏到用户空间;
  • 阻止对内核堆栈中未初始化变量的某些攻击;
  • 提供动态堆栈溢出检测工具。

此安全功能与内核自我保护项目(KSPP)的概念非常吻合:安全不仅仅是修复错误。 绝对不能解决代码中的所有错误,因此Linux内核应该在错误情况下安全地解决问题,包括在尝试利用漏洞时。 有关KSPP的更多详细信息,请参见项目Wiki

在grsecurity / PaX补丁程序中,STACKLEAK以PAX_MEMORY_STACKLEAK的形式出现。 但是,自2017年4月起,grsecurity / PaX补丁已停止免费分发。 因此,STACKLEAK在香草内核中的出现对于信息安全要求更高的Linux用户而言将是有价值的。

工单:

  • 从grsecurity / PaX补丁中选择STACKLEAK,
  • 仔细研究代码并形成补丁,
  • 发送给LKML,获取反馈意见,加以改进,然后在主线接受之前再次重复。

在撰写本文时(2018年9月25日),已发送一系列补丁的版本15 。 它包含架构上独立的部分以及x86_64和x86_32的代码。 由Red Hat的Laura Abbott开发的对arm64的STACKLEAK支持已经成功进入香草内核4.19。

STACKLEAK:安全功能


清除内核堆栈中的残留信息


这种措施减少了从核燃料电池堆到用户空间的某些泄漏可能产生的有用信息。

图1给出了一个从内核堆栈泄漏信息的示例。



方案1。

但是,如果在系统调用结束时内核堆栈的已用部分填充了固定值(图2),则这种类型的泄漏将变得无用。



方案2。

结果,STACKLEAK阻止了对内核堆栈中未初始化变量的某些攻击。 此类漏洞的示例:CVE-2017-17712,CVE-2010-2963。 我们可以 Kees Cook 的文章中找到有关CVE-2010-2963漏洞利用方法的描述。

图3显示了对内核堆栈中未初始化变量的攻击的实质。



方案3。

STACKLEAK会阻止此类攻击,因为在系统调用结束时填充核堆栈的值表示虚拟地址空间中的未使用区域(图4)。



方案4。

一个重要的限制是STACKLEAK不能防止在单个系统调用中执行类似的攻击。

内核内内核堆栈溢出检测


在普通内核(Linux内核主线)中,STACKLEAK仅与CONFIG_THREAD_INFO_IN_TASK和CONFIG_VMAP_STACK结合使用才能有效地防止内核堆栈深度溢出。 这两项措施均由Andy Lutomirski实施。

图5显示了利用这种类型漏洞的最简单版本。



方案5。

覆盖核栈底部的thread_info结构中的某些字段可以增加进程的特权。 但是,启用CONFIG_THREAD_INFO_IN_TASK选项时,将从核堆栈中删除此结构,从而消除了上述利用漏洞的方法。

此攻击的更高级版本是通过超出堆栈边界来覆盖相邻内存区域中的数据。 有关此方法的更多信息:


这种攻击类型如图6所示。



方案6。

在这种情况下,保护为CONFIG_VMAP_STACK。 启用此选项后,将在核堆栈旁边放置一个特殊的内存页面(保护页面),访问该页面会导致异常(图7)。



方案7。

最后,最深层溢出堆栈的最有趣的选择是像Stack Clash这样的攻击。 Gael Delalleau早在2005年就提出了这个想法。

在2017年,Qualys公司的研究人员对其进行了重新思考, 此技术为Stack Clash。 事实是,有一种方法可以跳过保护页并覆盖相邻存储区中的数据(图8)。 这是使用可变长度数组(VLA)完成的,其大小由攻击者控制。



方案8。

有关STACKLEAK和Stack Clash的更多信息, 请参见grsecurity博客

STACKLEAK如何防止核堆栈中的堆栈冲突? 在每次调用alloca()之前,都要检查堆栈的深度溢出。 这是修补程序系列的版本14中的相应代码:

void __used stackleak_check_alloca(unsigned long size) { unsigned long sp = (unsigned long)&sp; struct stack_info stack_info = {0}; unsigned long visit_mask = 0; unsigned long stack_left; BUG_ON(get_stack_info(&sp, current, &stack_info, &visit_mask)); stack_left = sp - (unsigned long)stack_info.begin; if (size >= stack_left) { /* * Kernel stack depth overflow is detected, let's report that. * If CONFIG_VMAP_STACK is enabled, we can safely use BUG(). * If CONFIG_VMAP_STACK is disabled, BUG() handling can corrupt * the neighbour memory. CONFIG_SCHED_STACK_END_CHECK calls * panic() in a similar situation, so let's do the same if that * option is on. Otherwise just use BUG() and hope for the best. */ #if !defined(CONFIG_VMAP_STACK) && defined(CONFIG_SCHED_STACK_END_CHECK) panic("alloca() over the kernel stack boundary\n"); #else BUG(); #endif } } 

但是,此功能已从版本15中排除。 这主要是由于Linus Torvalds在Linux内核安全补丁中禁止使用BUG_ON()引起争议的。

此外,补丁程序系列的第9版引发了讨论,因此决定从主线内核中删除所有变量数组。 大约有15个开发人员参与了这项工作,并将很快完成

STACKLEAK性能影响


我给出了x86_64上的性能测试结果。 设备:Intel Core i7-4770,16 GB RAM。

测试#1,诱人:在单个处理器内核上构建Linux内核

  # time make   4.18: real 12m14.124s user 11m17.565s sys 1m6.943s   4.18+stackleak: real 12m20.335s (+0.85%) user 11m23.283s sys 1m8.221s 

2号测试,没有吸引力:

  # hackbench -s 4096 -l 2000 -g 15 -f 25 -P    4.18: 9.08     4.18+stackleak: 9.47  (+4.3%) 

因此,STACKLEAK对系统性能的影响取决于负载的类型。 特别是,大量的短系统调用会增加开销。 T.O. 生产前必须针对计划的负载评估STACKLEAK性能。

内部设备STACKLEAK


STACKLEAK包括:

  • 在系统调用结束时清除内核堆栈的代码(最初用汇编器编写),
  • GCC插件,用于内核代码的工具编译。

清除内核堆栈是在stackleak_erase()函数中完成的。 在系统调用后返回用户空间之前,该函数完成。 将STACKLEAK_POISON(-0xBEEF)写入线程堆栈的已用部分。 在stackleak_track_stack()中不断更新的lowest_stack变量指向清理开始点。

方案9和方案10反映了stackleak_erase()的各个阶段。



方案9。



方案10。

T.O. stackleak_erase()仅清除核堆的已用部分。 这就是STACKLEAK这么快的原因。 而且,如果在每次系统调用结束时清除x86_64上所有16 kB的内核堆栈,hackbench的性能将下降40%。

在编译阶段对内核代码的检测是在STACKLEAK GCC插件中执行的。

GCC插件是GCC编译器的项目特定的可下载模块。 他们在GCC Pass Manager中注册新的通行证,为这些通行证提供回调。

因此,对于成熟的STACKLEAK操作,将对stackleak_track_stack()的调用插入具有大堆栈帧的函数的代码中。 另外,在每个alloca()之前,插入对已经提到的stackleak_check_alloca()的调用,然后,插入对stackleak_track_stack()的调用。

如前所述,在补丁系列的第15版中,从GCC插件中排除了对stackleak_check_alloca()的调用的插入。

Linux内核主线中的路径


主线中的STACKLEAK路径非常长且困难(图11)。



方案11. Linux内核主线中STACKLEAK的实现进展。

2017年4月,grsecurity的创建者为社区关闭了补丁程序,开始仅以商业方式分发它们。 2017年5月,我决定承担将STACKLEAK引入香草核心的任务。 因此开始了超过一年的旅程。 我在其中工作的Positive Technologies公司让我有机会在我的一些工作时间里完成这项任务。 但基本上,我在上面花了“空闲”时间。

自去年五月以来,我的补丁系列经历了多次审查,发生了重大变化,遭到莱纳斯·托瓦尔兹(Linus Torvalds)的两次批评。 我想离开这件事很多次。 但是在某个时刻,人们强烈希望达到终点。 在撰写本文时(2018年9月25日),修补程序系列的第15版位于linux-next分支中,满足Linus所述的所有要求,并已准备好用于4.20 / 5.0内核的合并窗口。

一个月前,我在Linux安全峰会上谈到了这项工作。 我提供了幻灯片视频的链接:


结论


STACKLEAK是一项非常有用的Linux内核安全性功能,可立即阻止利用多种类型的漏洞。 此外,PaX Team的原始作者能够使其在工程设计中既快速又美观。 因此,STACKLEAK在香草内核中的出现对于信息安全要求更高的Linux用户而言将是有价值的。 而且,朝这个方向进行的工作引起了Linux开发人员社区对内核自卫工具的关注。

聚苯乙烯


STACKLEAK最终被Linux 4.20内核采用:
git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=2d6bb6adb714b133db92ccd4bfc9c20f75f71f3f

支持的体系结构是x86_64,x86_32和arm64。

此外,已经完成了从Linux内核代码中消除可变长度数组的工作。 Gcc编译器警告“ -Wvla”包含在内核版本4.20中: lkml.org/lkml/2018/10/28/189

Source: https://habr.com/ru/post/zh-CN424633/


All Articles