zoukankan      html  css  js  c++  java
  • 【转】cve-2013-2094 perf_event_open 漏洞分析

    cve-2013-2094是于2013年4月前后发现的linux kernel本地漏洞,该漏洞影响3.8.9之前开启了PERF_EVENT的linux系统。利用该漏洞,通过perf_event_open系统调用,本地用户可以获得系统的最高权限。

    发生漏洞的是linux kernel中的perf event子系统,perf event是linux系统中提供性能分析接口的子系统,于2.6.31之后引入linux,之前被称为 Performance Counters for Linux (PCL)。通过syscall函数,用户可以调用perf event子系统提供的功能。由于perf event中的漏洞,用户可以通过精心构造的调用在任何内存地址写入数据,从而获得系统的最高权限。

    引起cve-2013-2094漏洞的是位于kernel/events/core.c文件中的perf_swevent_init函数。当使用syscall打开perf_event的file descriptor时,该函数被调用。

    在受影响的版本中,core.c关键部分内容如下:

    static int perf_swevent_init(struct perf_event *event)
    {
        int event_id = event->attr.config;  //dangerous!
    
        /*
         * ....omit some code...
         */
    
        if (event_id >= PERF_COUNT_SW_MAX)
            return -ENOENT;
    
        /*
         * .........omit........
         */
    
    }
    

    由于将event_id定义为了有符号型整数,而后面又只检查了event_id的上界,所以当值为负时,即可越过检查,继续执行后面的代码

    if (!event->parent) {
        int err;
    
        err = swevent_hlist_get(event);
        if (err)
            return err;
    
        atomic_inc(&perf_swevent_enabled[event_id]);
        event->destroy = sw_perf_event_destroy;
    }
    

    其中,static_key_slow_inc函数将某地址内容做inc。

    当使用负数值的event_id调用时,perf_swevent_enabled[event_id]将指向内核空间,如,在Cent OS 64bit系统中,当event_id = -1调用时,perf_swevent_enabled[event_id]的地址为:

    0xfffffffffffffffe * 4 + 0xffffffff81f360c0 == 0xFFFFFFFF81F360B8
    

    该地址指向内核空间。

    而当file descriptor关闭时,由于event_id定义为u64型,所以关闭时做dec时,地址指向的是用户空间。

    所以当我们用负数值调用perf_event_open时,将可以在perf_swevent_enabled地址后面任意地址写入数据。

    x64下的利用思路

    x64架构下,该漏洞比较容易利用,需要的信息更少,exploit执行更快。方法简单来说是修改IDT中 int 4 的中断向量。x64的linux中,Offset hight bits和Offset middle bits是分别保存的,而将 int 4 的高位 0xffffffff 加1之后,中断向量将指向用户空间。而将shellcode放置在用户空间的这段内存上,即可获得系统最高权限。

    具体来说,

    1. 使用-1和-2调用,得到数组的偏移值。

      使用-1和-2调用,在file descriptor被关闭后,将使 0x38000000 附近的内存中某两个值减1,通过搜索可以确定perf_swevent_enabled的编译值,并保存起来方便利用。

    2. 将shellcode放入适当的用户空间内存中

      使用 sidt 指令可以获得IDT的地址,然后使用 mmap 将 shellcode 放入适当的地址。使 IDT 中的中断向量高位+1之后落在shellcode上。

    3. 修改shellcode

      将 shellcode 中的 MARKER 修改为实际的 uid 和 gid 的保存地址。这样当shellcode运行时,即可找到 task_struct 和 thread_info 的地址,从而直接修改 real_cred 值,使当前进程获得 root 权限。

    4. 修改 IDT

      从 1 中得到的信息,和IDT 地址做计算,用计算好的负数值调用 perf_event_open ,可以使 int 4 的中断向量的高位 +1,从而指向shellcode。

    5. int 0x4

      触发shellcode

    6. 在shellcode中提权,并弹shell

      修改 real_cred 获得root权限,并修复被修改的 IDT 和其他部分内存,

      最后execl("/bin/bash", "-sh", NULL);

      得到root权限的shell

    移植到android系统

    Android采用了linux内核,且更新较linux在PC上的发行版慢。即使linux内核在2013年4月13日修复了该漏洞,原生Android系统和大量基于Android系统的第三方ROM仍然存在该漏洞。

    在ARM linux下,该漏洞仍然存在,仍然可以通过负数的event_id将任意内存地址+1。

    但由于手机使用的ARM架构和x64架构下,linux内核代码有所不同,该漏洞的利用方式也有所不同。

    但是在ARM linux下,IDT的结构如下

    struct _idt_entry {
      unsigned short base_lo;
      unsigned short sel;
      unsigned char unused;
      unsigned char flags;
      unsigned short base_hi;
    } __attribute__((packed));
    

    注意到base_hi是short型,而perf_swevent_enabled是int型的数组,所以无法将地址修改base_hi到用户空间地址。

    所以在ARM下我们没有修改IDT,而是在linux内核中寻找合适的函数指针。反复调用perf_event_open,将函数指针的值加到用户能控制的地址,然后调用函数。ptmx_fops结构中的fsync函数指针被初始化为0,且不需要特殊的权限即可调用。

    利用的基本思路是:

    /* file_operations 结构fsync函数的偏移为56 */
    int target = pmtx_ops + 56;
    int payload = -((perf_table - target)/4)
    
    struct perf_event_attr event_attr;
    event_attr.config = payload;
    ...
    
    /* 反复调用将fsync的值修改为合适的值 */
    syscall(__NR_perf_event_open, &event_attr, 0, -1, -1, 0);
    ...
    
    /* 调用 */
    int ptmx = open("/dev/ptmx", O_RDWR);
    fsync(ptmx);
    

    但是,linux系统中单个进程能够打开的file descriptor数量是有限制的,所以需要fork出足够多的进程,反复修改。

    受影响的ROM

    目前,测试了三款国内比较流行的手机,共4个ROM,该漏洞的情况如下:

    || Nexus 4 官方Rom 4.2.1|| 成功 || || Nexus 4 Cyanogenmod 10.1.2|| 内核已修补 || || Lenove 860i 官方ROM || 内核不支持perf_event || || Huawei U9508 官方ROM || 内核不支持perf_event ||

    CVE-2013-2094 exploit for Android 

  • 相关阅读:
    从零开始学SQLSERVER-游标CURSOR(基础)
    从零开始学SQLSERVER-创建表
    从零开始学HTML-button
    从零开始学前端HTML-input标签
    专项测试实战 | 如何测试 App 流畅度(基于 FPS 和丢帧率)
    接口测试框架实战 | 流程封装与基于加密接口的测试用例设计
    软件测试之30分钟轻松搞定正则表达式基础
    Python 装饰器填坑指南 | 最常见的报错信息、原因和解决方案
    react16与react15之间的区别
    react-父子组件之间的交互传值
  • 原文地址:https://www.cnblogs.com/jiayy/p/4444825.html
Copyright © 2011-2022 走看看