IT博客汇
  • 首页
  • 精华
  • 技术
  • 设计
  • 资讯
  • 扯淡
  • 权利声明
  • 登录 注册

    Linux drm_legacy_lock_free 空指针引用bug分析

    niubl发表于 2015-09-25 02:44:08
    love 0

    1. 漏洞描述

    Linux 在打开显卡设备使用ioctl()函数控制显卡时会调用DRM驱动程序,其中在调用drm_legacy_lock_free()函数时,获取lock指针的值赋值给old变量时,未考虑lock值可能会存在0值,导致空指针引用bug。

    2. 漏洞影响

    Linux kernel 4.2(Arch测试)
    Linux kernel 3.19.0-28 (Ubuntu测试)

    3. 漏洞分析

    该bug使用Trinity fuzz发现,Trinity是一款Linux 系统调用fuzz工具,用来测试Linux系统调用,运行Trinity:

     ./trinity -X -C 16

    Trinity X参数意图使root权限运行Trinity时切换到nogroup低权限运行Trinity进程,可是当我这样运行Trinity发现bug时,在POC重现时发现只能以root权限触发,原因以后分析。

    运行Trinity可能会导致系统崩溃,也正是我们发现bug的最好时机,系统崩溃后如何发现崩溃原因是我们关注的,这里采用Ubuntu下的linux-crashdump软件,保存系统崩溃现场。

    sudo apt-get install linux-crashdump

    系统崩溃后会自动生成dump,保存在/var/crash目录,并重启系统,现在使用crash工具分析dump,在使用crash分析dump时需要系统的调试符号表,安装如下:

    sudo tee /etc/apt/sources.list.d/ddebs.list << EOF
    deb http://ddebs.ubuntu.com/ $(lsb_release -cs)          main restricted universe multiverse
    deb http://ddebs.ubuntu.com/ $(lsb_release -cs)-security main restricted universe multiverse
    deb http://ddebs.ubuntu.com/ $(lsb_release -cs)-updates  main restricted universe multiverse
    deb http://ddebs.ubuntu.com/ $(lsb_release -cs)-proposed main restricted universe multiverse
    EOF

    sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys ECDCAD72428D7C01
    sudo apt-get update
    sudo apt-get install linux-image-$(uname -r)-dbgsym

    国内下载速度很慢,也可以在这里手动下载后安装,以Ubuntu 内核3.19.0-28举例:

    https://launchpad.net/ubuntu/trusty/amd64/linux-image-3.19.0-28-lowlatency-dbgsym/3.19.0-28.30~14.04.1

    安装完成后分析dump,crash装载dump文件,运行crash工具需要root权限:

     crash /usr/lib/debug/boot/vmlinux-3.19.0-28-generic 201509161541/dump.201509161541

    装载如图:

    图片1

    运行bt命令查看调用栈,可以看到崩溃发生在函数drm_legacy_lock_free()函数中,#8下面:

    图片2

    反汇编drm_legacy_lock_free()函数:

    图片3

    结合内核崩溃时的寄存器值,从上面的反汇编代码中可以看出drm_legacy_lock_free+64代码处把%rbx(寄存器,后面省略)的值赋值给%ecx,而%rbx此时的值为0,导致空指针引用bug。%rbx来自%rdi的值,在drm_legacy_lock_free+30代码处可以看出,而%rdi就是drm_legacy_lock_free()函数的第一个参数(参见Linux x86_64寄存器参数传递方法),%rbx就是drm_legacy_lock_free()函数中的lock指针,%rdi就是drm_legacy_lock_free()函数参数lock_data的值,而%rdi的值呢,我们可以看到在drm_legacy_lock_free+11代码处和drm_legacy_lock_free+17代码处分别传递给了%14,%13,观察系统崩溃现场寄存器可以看到两个寄存器的值,因此%rdi值为0xffff8800c2480040。

    Linux内核源代码源码地址:
    http://lxr.free-electrons.com/source/drivers/gpu/drm/drm_lock.c#L255
    顺着调用栈向上,来到调用drm_legacy_lock_free()函数的drm_legacy_unlock()函数处,反汇编代码:

    图片4

    可以看到drm_legacy_unlock+23代码处调用了drm_legacy_lock_free()函数,在调用之前做了一些参数设置准备,drm_legacy_unlock+19代码处给%rdi加0x40,drm_legacy_unlock+8代码处把%rdx+0x78的值赋值给%rdi,从drm_legacy_lock_free()函数的分析中我们知道%rdi的值为0xffff8800c2480040,那么现在%rdx+0x78的值可以推测出来,即0xffff8800c2480000,即master指针指向的,那么%rdx是多少,%rdx是drm_legacy_unlock()函数的第三个参数(参见Linux x86_64寄存器参数传递方法),结合内核源码可以看出%rdx就是参数 file_priv的值。

    Linux内核源代码源码地址:
    http://lxr.free-electrons.com/source/drivers/gpu/drm/drm_lock.c#L151
    顺着调用栈再向上,来到调用drm_legacy_unlock()函数的drm_ioctl()函数处,反汇编:

    图片5

    由调用栈可以看出在drm_ioctl+810代码处调用了drm_legacy_unlock()函数,他的第三个参数%rdx是由%r15赋给的,%r15的值在drm_ioctl()函数的开头可以找到,如图:

    图片6

    上图中,%rdi+0xd0的值赋值给了%r15,%rdi是drm_ioctl()函数的第一个参数(参见Linux x86_64寄存器参数传递方法),结合内核源码可以看出即是filp指针,filp指针是一个file类型的指针,%rdi+0xd0的值即是filp->private_data(file_priv指向它),那么可以说filep->private_data->->file_priv->master的值是0xffff8800c2480000,我们在内存中寻找他

    search -t 0xffff8800c2480000

    图片7

    有四个结果,我已经找到了正确的,第二个地址0xffff8800933c2078,我们看看它:

    图片8

    master是file_priv的drm_file结构体成员,看drm_file结构(http://lxr.free-electrons.com/source/include/drm/drmP.h#L289)

    struct drm_file {
    290         unsigned authenticated :1;
    291         /* Whether we're master for a minor. Protected by master_mutex */
    ……
    310         /** Mapping of mm object handles to object pointers. */
    311         struct idr object_idr;
    312         /** Lock for synchronization of access to object_idr. */
    313         spinlock_t table_lock;
    314 
    315         struct file *filp;
    316         void *driver_priv;
    317 
    318         struct drm_master *master; /* master this node is currently associated with
    319                                       N.B. not always minor->master */
    320         /**
    ……

    可以看出结构体中,在master的上面第315行代码处有filep的地址,那么从刚才的内存中可以看出,filep的值为0xffff88003b3cfa00,验证一下:

    struct file 0xffff88003b3cfa00

    图片9

    上图中显示为0xffff88003b3cfa00地址按照file结构体显示的数据,其中private_data值为0xffff8800933c2000,查看0xffff8800933c2000内存,找到0xffff8800c2480000的值,证明filep的值正确。filep就是打开文件的文件描述符,地址0xffff88003b3cfa00,那么我们可以看看系统崩溃时打开了那些文件,crash中运行files命令:

    图片10

    图片11

    由上图可以看出filep文件描述符的地址赫然在列,句柄688,路径是/dev/dri/card0。

    回头看下调用栈:

    图片12

    上图中可以看出,#13在用户态的寄存器现场中,传递给ioctl()函数的第一个参数%rdi,他的值就是0x2b0,十进制就是688,也就说明正是他打开了/dev/dri/card0设备文件,那么POC已经可以写出了:

    #include <fcntl.h>
    #include <errno.h>
    #include <stdio.h>
    
    main()
    {
        int fd;
        int i = 20;
        fd = open("/dev/dri/card0", O_RDONLY);
        ioctl(fd, 0x4008642b, &i);
        fprintf(stderr, "the errno is %d\n", errno);
    }

    gcc编译后执行会导致系统崩溃,执行需要root权限。

    4. 相关资源链接

    1. https://wiki.ubuntu.com/Kernel/CrashdumpRecipe
    2.http://lxr.free-electrons.com/
    3.https://github.com/kernelslacker/trinity
    4.http://codemonkey.org.uk/projects/trinity/
    5.https://zh.wikipedia.org/wiki/X86%E8%B0%83%E7%94%A8%E7%BA%A6%E5%AE%9A
    6.https://launchpad.net/ubuntu/trusty/amd64/
    7.https://bugzilla.kernel.org/show_bug.cgi?id=104831



沪ICP备19023445号-2号
友情链接