首页 服务器系统 Linux

Linux内核漏洞详情及其Exp

概述

本文中详细介绍了Linux内核新引入的io_uring子系统中的UAF漏洞CVE-2021-20226。该漏洞导致任意文件结构释放后重用,可用于在内核中提升权限,影响范围为Linux内核v5.6至v5.7。

漏洞详情

Linux内核v5.1引入了一个名为io_uring的新异步I/O接口。io_uring通过批处理I/O操作系统调用运行,因此可以在一个系统调用中执行多个I/O操作。

Linux内核v5.6中的IORING_OP_CLOSE操作实现中存在漏洞。当系统调用将files_struct传递给内核线程时,io_grab_files()不会在注释(1)处增加参考计数,从而导致释放的文件结构在之后被访问。

static int io_grab_files(struct io_kiocb *req) 
{ 
// ...      
     rcu_read_lock();     
     spin_lock_irq(&ctx->inflight_lock);spin_lock_irq(&ctx->inflight_lock);      
     
     
     if (fcheck(ctx->ring_fd) == ctx->ring_file) {         
         list_add(&req->inflight_entry, &ctx->inflight_list);         
         req->flags |= REQ_F_INFLIGHT;         
         req->work.files = current->files;  // <-- (1)         
         ret = 0;     
      }     
      spin_unlock_irq(&ctx->inflight_lock);     
      rcu_read_unlock();      
      
      return ret; 
 }

漏洞利用(Exp)

map_lookup_elem()和map_update_elem()函数都可以用于利用该漏洞。

static int map_lookup_elem(union bpf_attr *attr) 
{     
      void __user *ukey = u64_to_user_ptr(attr->key);     
      int ufd = attr->map_fd; 
// ...     
      f = fdget(ufd);          // <-- (2)     
      map = __bpf_map_get(f); 
// ...     
      key = __bpf_copy_key(ukey, map->key_size); key = __bpf_copy_key(ukey, map->key_size); // <-- (3)     
      if (IS_ERR(key)) {         
          err = PTR_ERR(key);         
          goto err_put;     
       }      
       
       value_size = bpf_map_value_size(map);  // <-- (4)      
       
       err = -ENOMEM;     
       value = kmalloc(value_size, GFP_USER | __GFP_NOWARN);     
       if (!value)         
            goto free_key;      
            
       err = bpf_map_copy_value(map, key, value, attr->flags); // <-- (5)     
       if (err)         goto free_value;      
       
       err = -EFAULT;     
       if (copy_to_user(uvalue, value, value_size) != 0) // <-- (6)         
           goto free_value; 
   // ... 
   }

注释(2)处的fdget()是一个优化函数,如果当前任务是单线程的,则不会增加参考计数。返回的文件结构 f 可以在之后的IORING_OP_CLOSE中释放。注释(3)处的__bpf_copy_key()系统调用实际上是copy_from_user()的封装。这使我们能够使用userfaultfd生成竞争条件,并触发漏洞。此时,文件结构 f 及其对应的映射被释放。可通过注释(4)和(5)处的虚假数据重新分配映射的内存。最终,我们可以在注释(6)处读取任意内存并向用户模式暴露。

该漏洞利用时间表如下所示:

recvmsg()函数用于定时控制。通过喷射setxattr()即可伪造被释放的bpf_map。通过map_update_elem()即可实现任意写入。fdget()条件的存在,使得该漏洞利用方法仅限于单核环境

相关推荐