linux内核UAF漏洞分析之CVE-2018-17182

本文深入分析了Linux内核中的Use-After-Free(UAF)漏洞CVE-2018-17182,详细解释了vma缓存机制及其两次优化过程,以及如何导致的安全隐患。文章还介绍了漏洞的修复方法和PoC执行流程,展示了利用该漏洞进行提权的过程。
摘要由CSDN通过智能技术生成

1. 漏洞原理分析

1.1 vma缓存机制

vma就是虚拟地址区域(virtual memory area),内核用vma来管理进程分配的虚拟内存地址。vma在内核中用结构体struct vm_ares_struct 表示,定义如下:

struct vm_area_struct {
   
          .....
          unsigned long vm_start; // 起始虚拟地址
          unsigned long vm_start; // 起始虚拟地址
          struct vm_area_struct *vm_next, *vm_prev; //同时是一个链表节点
          struct rb_node vm_rb; //同时也是一个红黑树节点
          ......
 }

一个进程有多个vma,这些vma如何管理呢,根据结构体的定义可知vma同时存在2种组织方式
1)链表,用于遍历vma
2)红黑树,用于查找某个虚拟地址所在的vma

在内核中有个find_vma函数,根据传入的虚拟地址addr,查找vma组成的红黑树,找到这个addr所在的vma并返回,也就是vm_start <= addr <= vm_end。这个函数在内核中调用的比较频繁,比如进程访问一个虚拟地址时,在内核里面都会先去找这个地址对应的vma。 如果每次find_vma都走红黑树查找,效率会比较低。vma缓存机制,就是为了优化find_vma这个函数的执行效率,这个机制将上次find_vma得到的vma缓存下来,下次find_vma时,先去缓存中找,找不到时再去走红黑树查找。

1.2 缓存机制第一次优化

https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=615d6e8756c87149f2d4c1b93d471bca002bd849

根据这个优化的patch可以看到,最原始的缓存机使用的是mm_struct中的mmap_cache,即mmap_cache指向上次find_vma函数返回的vma。但是在多线程环境下,所有线程都是共用一个mm_struct,也就是说所有线程共用一个缓存mmap_cache,那么这种情况下,缓存的命中率就不会很高。所以这个patch主要的思想就是给每个线程一个单独的缓存,互不影响。

diff --git a/include/linux/mm_types.h b/include/linux/mm_types.h
index 290901a..2b58d19 100644
--- a/include/linux/mm_types.h
+++ b/include/linux/mm_types.h
@@ -342,9 +342,9 @@ struct mm_rss_stat {
   
 
 struct kioctx_table;
 struct mm_struct {
   
- struct vm_area_struct * mmap;  /* list of VMAs */
+ struct vm_area_struct *mmap;  /* list of VMAs */
  struct rb_root mm_rb;
- struct vm_area_struct * mmap_cache; /*移除老的缓存方式*/
+ u32 vmacache_seqnum;                   /* per-thread vmacache */
 #ifdef CONFIG_MMU
  unsigned long (*get_unmapped_area) (struct file *filp,
     unsigned long addr, unsigned long len,

mm_struct里面新增的vmacache_seqnum后面再讲作用,继续往下看补丁。由于每个线程都对应一个单独的task_struct,所以将缓存放到了task_struct里面,即vmacache,而且为task_struct结构体新增了一个u32类型的成员变量vmacache_seqnum。

@@ -1235,6 +1239,9 @@ struct task_struct {
   
 #ifdef CONFIG_COMPAT_BRK
  unsigned brk_randomized:1;
 #endif
+ /* per-thread vma caching */
+ u32 vmacache_seqnum;
+ struct vm_area_struct *vmacache[VMACACHE_SIZE];
 #if defined(SPLIT_RSS_COUNTING)
  struct task_rss_stat rss_stat;
 #endif

线程之间共用虚拟地址空间,即共用一个mm_struct,vma也都是共用的。 task_struct,mm_struct,vma_area_struct的关系如下图:
在这里插入图片描述
思考一个问题,如果一个vma被一个线程释放掉了,但是这个vma还在另外一个线程的缓存中,而这个线程不知道它自己缓存的vma被释放掉了,后面这个线程在find_vma的时候,如果要查找的地址刚好在这个vma中,那么就会把这个已经释放了的vma返回,这样就产生了UFA(Use After Free),所以需要一个同步机制。

继续看这个patch,在task_struct和mm_strcut这2个结构体中都新增了一个u32的成员变量vmacache_sequm,用来做同步,机制如下:
1)当有vma被某个线程给释放了,将mm_struct中的vmacache_sequm加1
2)线程在find_vma中使用缓存时,先比较task_struct和mm_struct中的vmacache_sequm,如果不相等,说明vma有变化,清空缓存,走红黑树查找vma

当一个vma被释放的时候,调用vmacache_invalidate将mm_strcut中的vmacache_seqnum加1,但是vmacache_sequm是一个u32类型的变量,如果一直加1,是有可能发生溢出的。所以在这个函数中有个检测溢出的处理,如果发生溢出,就调用vmacache_flush_all遍历所有线程将他们的vmacache都清空,然后vmacache_sequm又从0开始加1。

+static inline void vmacache_invalidate(struct mm_struct *mm)
+{
   
+ mm->vmacache_seqnum++;
+
+ /* 处理溢出 */
+ if (unlikely(mm->vmacache_seqnum 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值