RSS Feed
更好更安全的互联网

Linux drm_legacy_lock_free 空指针引用bug分析

2015-09-25

 tuxwhitebg

知道创宇安全研究团队  niubl:2015.9.25


 

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

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

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

国内下载速度很慢,也可以在这里手动下载后安装,以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权限:

装载如图:

图片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,我们在内存中寻找他

图片7

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

图片8

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

可以看出结构体中,在master的上面第315行代码处有filep的地址,那么从刚才的内存中可以看出,filep的值为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已经可以写出了:

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

该bug已经提交给linux官方,目前仍未修复:https://bugzilla.kernel.org/show_bug.cgi?id=104831

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

作者:niubl | Categories:安全研究 | Tags:

发表评论